From 40425ec176aab1f8d14220ff0b7ee8218b341ac0896f06a5f55f11e0d10945f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Mon, 2 Oct 2023 14:14:56 +0000 Subject: [PATCH] Accepting request 1114819 from home:vizhestkov:branches:systemsmanagement:saltstack - Fix optimization_order opt to prevent testsuite fails - Imporve salt.utils.json.find_json to avoid fails with transactional salt salt-ssh managed clients (bsc#1213293) - Use salt-call from salt bundle with transactional_update - Only call native_str on curl_debug message in tornado when needed - Implement the calling for batch async from the salt CLI - Added: * implement-the-calling-for-batch-async-from-the-salt-.patch * imporve-salt.utils.json.find_json-bsc-1213293.patch * only-call-native_str-on-curl_debug-message-in-tornad.patch * use-salt-call-from-salt-bundle-with-transactional_up.patch * fix-optimization_order-opt-to-prevent-test-fails.patch OBS-URL: https://build.opensuse.org/request/show/1114819 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=218 --- _lastrevision | 2 +- ...tion_order-opt-to-prevent-test-fails.patch | 62 ++++++ ...lling-for-batch-async-from-the-salt-.patch | 145 +++++++++++++ ...alt.utils.json.find_json-bsc-1213293.patch | 204 ++++++++++++++++++ ..._str-on-curl_debug-message-in-tornad.patch | 31 +++ salt.changes | 17 ++ salt.spec | 10 + ...om-salt-bundle-with-transactional_up.patch | 103 +++++++++ 8 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 fix-optimization_order-opt-to-prevent-test-fails.patch create mode 100644 implement-the-calling-for-batch-async-from-the-salt-.patch create mode 100644 imporve-salt.utils.json.find_json-bsc-1213293.patch create mode 100644 only-call-native_str-on-curl_debug-message-in-tornad.patch create mode 100644 use-salt-call-from-salt-bundle-with-transactional_up.patch diff --git a/_lastrevision b/_lastrevision index 86a5427..200fa85 100644 --- a/_lastrevision +++ b/_lastrevision @@ -1 +1 @@ -d9e734d2a130c3a4f5e14f8e2e00be98052a41f9 \ No newline at end of file +283e739d11222a69bc566092b1b9d0d43ae59a52 \ No newline at end of file diff --git a/fix-optimization_order-opt-to-prevent-test-fails.patch b/fix-optimization_order-opt-to-prevent-test-fails.patch new file mode 100644 index 0000000..1b72186 --- /dev/null +++ b/fix-optimization_order-opt-to-prevent-test-fails.patch @@ -0,0 +1,62 @@ +From aaf593d17f51a517e0adb6e9ec1c0d768ab5f855 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 2 Oct 2023 14:24:27 +0200 +Subject: [PATCH] Fix optimization_order opt to prevent test fails + +--- + tests/pytests/unit/grains/test_core.py | 4 ++-- + tests/pytests/unit/loader/test_loader.py | 2 +- + tests/pytests/unit/test_config.py | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py +index 993c723950..36545287b9 100644 +--- a/tests/pytests/unit/grains/test_core.py ++++ b/tests/pytests/unit/grains/test_core.py +@@ -156,7 +156,7 @@ def test_network_grains_secondary_ip(tmp_path): + opts = { + "cachedir": str(cache_dir), + "extension_modules": str(extmods), +- "optimization_order": [0], ++ "optimization_order": [0, 1, 2], + } + with patch("salt.utils.network.interfaces", side_effect=[data]): + grains = salt.loader.grain_funcs(opts) +@@ -243,7 +243,7 @@ def test_network_grains_cache(tmp_path): + opts = { + "cachedir": str(cache_dir), + "extension_modules": str(extmods), +- "optimization_order": [0], ++ "optimization_order": [0, 1, 2], + } + with patch( + "salt.utils.network.interfaces", side_effect=[call_1, call_2] +diff --git a/tests/pytests/unit/loader/test_loader.py b/tests/pytests/unit/loader/test_loader.py +index f4a4b51a58..86348749db 100644 +--- a/tests/pytests/unit/loader/test_loader.py ++++ b/tests/pytests/unit/loader/test_loader.py +@@ -57,7 +57,7 @@ def test_raw_mod_functions(): + "Ensure functions loaded by raw_mod are LoaderFunc instances" + opts = { + "extension_modules": "", +- "optimization_order": [0], ++ "optimization_order": [0, 1, 2], + } + ret = salt.loader.raw_mod(opts, "grains", "get") + for k, v in ret.items(): +diff --git a/tests/pytests/unit/test_config.py b/tests/pytests/unit/test_config.py +index cb343cb75e..76d5605360 100644 +--- a/tests/pytests/unit/test_config.py ++++ b/tests/pytests/unit/test_config.py +@@ -16,7 +16,7 @@ def test_call_id_function(tmp_path): + "cachedir": str(cache_dir), + "extension_modules": str(extmods), + "grains": {"osfinger": "meh"}, +- "optimization_order": [0], ++ "optimization_order": [0, 1, 2], + } + ret = salt.config.call_id_function(opts) + assert ret == "meh" +-- +2.42.0 + diff --git a/implement-the-calling-for-batch-async-from-the-salt-.patch b/implement-the-calling-for-batch-async-from-the-salt-.patch new file mode 100644 index 0000000..128dea6 --- /dev/null +++ b/implement-the-calling-for-batch-async-from-the-salt-.patch @@ -0,0 +1,145 @@ +From 7ab208fd2d23eaa582cdbba912d4538d8c87e5f4 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 2 Oct 2023 13:24:15 +0200 +Subject: [PATCH] Implement the calling for batch async from the salt + CLI + +* Implement calling batch async with salt CLI + +* Add the test for calling batch async with salt CLI +--- + salt/cli/salt.py | 53 ++++++++++++++++++++++++++++- + tests/pytests/unit/cli/test_salt.py | 50 +++++++++++++++++++++++++++ + 2 files changed, 102 insertions(+), 1 deletion(-) + create mode 100644 tests/pytests/unit/cli/test_salt.py + +diff --git a/salt/cli/salt.py b/salt/cli/salt.py +index f90057f668..e19cfa5ce6 100644 +--- a/salt/cli/salt.py ++++ b/salt/cli/salt.py +@@ -47,7 +47,12 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): + self.exit(2, "{}\n".format(exc)) + return + +- if self.options.batch or self.options.static: ++ if self.options.batch and self.config["async"]: ++ # _run_batch_async() will just return the jid and exit ++ # Execution will not continue past this point ++ # in batch async mode. Batch async is handled by the master. ++ self._run_batch_async() ++ elif self.options.batch or self.options.static: + # _run_batch() will handle all output and + # exit with the appropriate error condition + # Execution will not continue past this point +@@ -296,6 +301,52 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): + retcode = job_retcode + sys.exit(retcode) + ++ def _run_batch_async(self): ++ kwargs = { ++ "tgt": self.config["tgt"], ++ "fun": self.config["fun"], ++ "arg": self.config["arg"], ++ "timeout": self.options.timeout, ++ "show_timeout": self.options.show_timeout, ++ "show_jid": self.options.show_jid, ++ "batch": self.config["batch"], ++ } ++ tgt = kwargs.pop("tgt", "") ++ fun = kwargs.pop("fun", "") ++ ++ if self.config.get("eauth", ""): ++ kwargs.update( ++ { ++ "eauth": self.config["eauth"], ++ } ++ ) ++ for opt in ("username", "password"): ++ if opt in self.config: ++ kwargs[opt] = self.config[opt] ++ ++ try: ++ ret = self.local_client.run_job(tgt, fun, **kwargs) ++ except ( ++ AuthenticationError, ++ AuthorizationError, ++ SaltInvocationError, ++ EauthAuthenticationError, ++ SaltClientError, ++ ) as exc: ++ ret = str(exc) ++ self.exit(2, "ERROR: {}\n".format(exc)) ++ if "jid" in ret and "error" not in ret: ++ salt.utils.stringutils.print_cli( ++ "Executed command with job ID: {}".format(ret["jid"]) ++ ) ++ else: ++ self._output_ret(ret, self.config.get("output", "nested")) ++ ++ if "error" in ret: ++ sys.exit(1) ++ ++ sys.exit(0) ++ + def _print_errors_summary(self, errors): + if errors: + salt.utils.stringutils.print_cli("\n") +diff --git a/tests/pytests/unit/cli/test_salt.py b/tests/pytests/unit/cli/test_salt.py +new file mode 100644 +index 0000000000..d9f4b5b097 +--- /dev/null ++++ b/tests/pytests/unit/cli/test_salt.py +@@ -0,0 +1,50 @@ ++import pytest ++ ++from tests.support.mock import MagicMock, patch ++ ++ ++def test_saltcmd_batch_async_call(): ++ """ ++ Test calling batch async with salt CLI ++ """ ++ import salt.cli.salt ++ ++ local_client = MagicMock() ++ local_client.run_job = MagicMock(return_value={"jid": 123456}) ++ with pytest.raises(SystemExit) as exit_info, patch( ++ "sys.argv", ++ [ ++ "salt", ++ "--batch=10", ++ "--async", ++ "*", ++ "test.arg", ++ "arg1", ++ "arg2", ++ "kwarg1=val1", ++ ], ++ ), patch("salt.cli.salt.SaltCMD.process_config_dir", MagicMock), patch( ++ "salt.output.display_output", MagicMock() ++ ), patch( ++ "salt.client.get_local_client", return_value=local_client ++ ), patch( ++ "salt.utils.stringutils.print_cli", MagicMock() ++ ) as print_cli: ++ salt_cmd = salt.cli.salt.SaltCMD() ++ salt_cmd.config = { ++ "async": True, ++ "batch": 10, ++ "tgt": "*", ++ "fun": "test.arg", ++ "arg": ["arg1", "arg2", {"__kwarg__": True, "kwarg1": "val1"}], ++ } ++ salt_cmd._mixin_after_parsed_funcs = [] ++ salt_cmd.run() ++ ++ local_client.run_job.assert_called_once() ++ assert local_client.run_job.mock_calls[0].args[0] == "*" ++ assert local_client.run_job.mock_calls[0].args[1] == "test.arg" ++ assert local_client.run_job.mock_calls[0].kwargs["arg"] == ["arg1", "arg2", {"__kwarg__": True, "kwarg1": "val1"}] ++ assert local_client.run_job.mock_calls[0].kwargs["batch"] == 10 ++ print_cli.assert_called_once_with("Executed command with job ID: 123456") ++ assert exit_info.value.code == 0 +-- +2.42.0 + diff --git a/imporve-salt.utils.json.find_json-bsc-1213293.patch b/imporve-salt.utils.json.find_json-bsc-1213293.patch new file mode 100644 index 0000000..6a62d3e --- /dev/null +++ b/imporve-salt.utils.json.find_json-bsc-1213293.patch @@ -0,0 +1,204 @@ +From d5a36df8db87be578b6bf258ff2dd5bf9b234e79 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 2 Oct 2023 13:26:20 +0200 +Subject: [PATCH] Imporve salt.utils.json.find_json (bsc#1213293) + +* Improve salt.utils.json.find_json + +* Move tests of find_json to pytest +--- + salt/utils/json.py | 39 +++++++- + tests/pytests/unit/utils/test_json.py | 122 ++++++++++++++++++++++++++ + 2 files changed, 158 insertions(+), 3 deletions(-) + create mode 100644 tests/pytests/unit/utils/test_json.py + +diff --git a/salt/utils/json.py b/salt/utils/json.py +index 33cdbf401d..0845b64694 100644 +--- a/salt/utils/json.py ++++ b/salt/utils/json.py +@@ -32,18 +32,51 @@ def find_json(raw): + """ + ret = {} + lines = __split(raw) ++ lengths = list(map(len, lines)) ++ starts = [] ++ ends = [] ++ ++ # Search for possible starts end ends of the json fragments + for ind, _ in enumerate(lines): ++ line = lines[ind].lstrip() ++ if line == "{" or line == "[": ++ starts.append((ind, line)) ++ if line == "}" or line == "]": ++ ends.append((ind, line)) ++ ++ # List all the possible pairs of starts and ends, ++ # and fill the length of each block to sort by size after ++ starts_ends = [] ++ for start, start_br in starts: ++ for end, end_br in reversed(ends): ++ if end > start and ( ++ (start_br == "{" and end_br == "}") ++ or (start_br == "[" and end_br == "]") ++ ): ++ starts_ends.append((start, end, sum(lengths[start : end + 1]))) ++ ++ # Iterate through all the possible pairs starting from the largest ++ starts_ends.sort(key=lambda x: (x[2], x[1] - x[0], x[0]), reverse=True) ++ for start, end, _ in starts_ends: ++ working = "\n".join(lines[start : end + 1]) + try: +- working = "\n".join(lines[ind:]) +- except UnicodeDecodeError: +- working = "\n".join(salt.utils.data.decode(lines[ind:])) ++ ret = json.loads(working) ++ except ValueError: ++ continue ++ if ret: ++ return ret + ++ # Fall back to old implementation for backward compatibility ++ # excpecting json after the text ++ for ind, _ in enumerate(lines): ++ working = "\n".join(lines[ind:]) + try: + ret = json.loads(working) + except ValueError: + continue + if ret: + return ret ++ + if not ret: + # Not json, raise an error + raise ValueError +diff --git a/tests/pytests/unit/utils/test_json.py b/tests/pytests/unit/utils/test_json.py +new file mode 100644 +index 0000000000..72b1023003 +--- /dev/null ++++ b/tests/pytests/unit/utils/test_json.py +@@ -0,0 +1,122 @@ ++""" ++Tests for salt.utils.json ++""" ++ ++import textwrap ++ ++import pytest ++ ++import salt.utils.json ++ ++ ++def test_find_json(): ++ some_junk_text = textwrap.dedent( ++ """ ++ Just some junk text ++ with multiline ++ """ ++ ) ++ some_warning_message = textwrap.dedent( ++ """ ++ [WARNING] Test warning message ++ """ ++ ) ++ test_small_json = textwrap.dedent( ++ """ ++ { ++ "local": true ++ } ++ """ ++ ) ++ test_sample_json = """ ++ { ++ "glossary": { ++ "title": "example glossary", ++ "GlossDiv": { ++ "title": "S", ++ "GlossList": { ++ "GlossEntry": { ++ "ID": "SGML", ++ "SortAs": "SGML", ++ "GlossTerm": "Standard Generalized Markup Language", ++ "Acronym": "SGML", ++ "Abbrev": "ISO 8879:1986", ++ "GlossDef": { ++ "para": "A meta-markup language, used to create markup languages such as DocBook.", ++ "GlossSeeAlso": ["GML", "XML"] ++ }, ++ "GlossSee": "markup" ++ } ++ } ++ } ++ } ++ } ++ """ ++ expected_ret = { ++ "glossary": { ++ "GlossDiv": { ++ "GlossList": { ++ "GlossEntry": { ++ "GlossDef": { ++ "GlossSeeAlso": ["GML", "XML"], ++ "para": ( ++ "A meta-markup language, used to create markup" ++ " languages such as DocBook." ++ ), ++ }, ++ "GlossSee": "markup", ++ "Acronym": "SGML", ++ "GlossTerm": "Standard Generalized Markup Language", ++ "SortAs": "SGML", ++ "Abbrev": "ISO 8879:1986", ++ "ID": "SGML", ++ } ++ }, ++ "title": "S", ++ }, ++ "title": "example glossary", ++ } ++ } ++ ++ # First test the valid JSON ++ ret = salt.utils.json.find_json(test_sample_json) ++ assert ret == expected_ret ++ ++ # Now pre-pend some garbage and re-test ++ garbage_prepend_json = f"{some_junk_text}{test_sample_json}" ++ ret = salt.utils.json.find_json(garbage_prepend_json) ++ assert ret == expected_ret ++ ++ # Now post-pend some garbage and re-test ++ garbage_postpend_json = f"{test_sample_json}{some_junk_text}" ++ ret = salt.utils.json.find_json(garbage_postpend_json) ++ assert ret == expected_ret ++ ++ # Now pre-pend some warning and re-test ++ warning_prepend_json = f"{some_warning_message}{test_sample_json}" ++ ret = salt.utils.json.find_json(warning_prepend_json) ++ assert ret == expected_ret ++ ++ # Now post-pend some warning and re-test ++ warning_postpend_json = f"{test_sample_json}{some_warning_message}" ++ ret = salt.utils.json.find_json(warning_postpend_json) ++ assert ret == expected_ret ++ ++ # Now put around some garbage and re-test ++ garbage_around_json = f"{some_junk_text}{test_sample_json}{some_junk_text}" ++ ret = salt.utils.json.find_json(garbage_around_json) ++ assert ret == expected_ret ++ ++ # Now pre-pend small json and re-test ++ small_json_pre_json = f"{test_small_json}{test_sample_json}" ++ ret = salt.utils.json.find_json(small_json_pre_json) ++ assert ret == expected_ret ++ ++ # Now post-pend small json and re-test ++ small_json_post_json = f"{test_sample_json}{test_small_json}" ++ ret = salt.utils.json.find_json(small_json_post_json) ++ assert ret == expected_ret ++ ++ # Test to see if a ValueError is raised if no JSON is passed in ++ with pytest.raises(ValueError): ++ ret = salt.utils.json.find_json(some_junk_text) +-- +2.42.0 + diff --git a/only-call-native_str-on-curl_debug-message-in-tornad.patch b/only-call-native_str-on-curl_debug-message-in-tornad.patch new file mode 100644 index 0000000..6821c6a --- /dev/null +++ b/only-call-native_str-on-curl_debug-message-in-tornad.patch @@ -0,0 +1,31 @@ +From b76b74bd9640adf3b6798e4de4b89aaa7af62c9f Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 2 Oct 2023 13:24:43 +0200 +Subject: [PATCH] Only call native_str on curl_debug message in tornado + when needed + +Co-authored-by: Ben Darnell +--- + salt/ext/tornado/curl_httpclient.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/salt/ext/tornado/curl_httpclient.py b/salt/ext/tornado/curl_httpclient.py +index 8652343cf7..9e4133fd13 100644 +--- a/salt/ext/tornado/curl_httpclient.py ++++ b/salt/ext/tornado/curl_httpclient.py +@@ -494,10 +494,11 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): + + def _curl_debug(self, debug_type, debug_msg): + debug_types = ('I', '<', '>', '<', '>') +- debug_msg = native_str(debug_msg) + if debug_type == 0: ++ debug_msg = native_str(debug_msg) + curl_log.debug('%s', debug_msg.strip()) + elif debug_type in (1, 2): ++ debug_msg = native_str(debug_msg) + for line in debug_msg.splitlines(): + curl_log.debug('%s %s', debug_types[debug_type], line) + elif debug_type == 4: +-- +2.42.0 + diff --git a/salt.changes b/salt.changes index c0cfea3..a8974b2 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,20 @@ +------------------------------------------------------------------- +Mon Oct 2 12:49:41 UTC 2023 - Victor Zhestkov + +- Fix optimization_order opt to prevent testsuite fails +- Imporve salt.utils.json.find_json to avoid fails + with transactional salt salt-ssh managed clients (bsc#1213293) +- Use salt-call from salt bundle with transactional_update +- Only call native_str on curl_debug message in tornado when needed +- Implement the calling for batch async from the salt CLI + +- Added: + * implement-the-calling-for-batch-async-from-the-salt-.patch + * imporve-salt.utils.json.find_json-bsc-1213293.patch + * only-call-native_str-on-curl_debug-message-in-tornad.patch + * use-salt-call-from-salt-bundle-with-transactional_up.patch + * fix-optimization_order-opt-to-prevent-test-fails.patch + ------------------------------------------------------------------- Mon Oct 2 09:50:00 UTC 2023 - Pablo Suárez Hernández diff --git a/salt.spec b/salt.spec index 757d9fc..22784c8 100644 --- a/salt.spec +++ b/salt.spec @@ -306,6 +306,16 @@ Patch79: revert-usage-of-long-running-req-channel-bsc-1213960.patch Patch80: write-salt-version-before-building-when-using-with-s.patch # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65036 Patch81: fix-calculation-of-sls-context-vars-when-trailing-do.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/594 +Patch82: implement-the-calling-for-batch-async-from-the-salt-.patch +# PATCH-FIX_UPSTREAM: https://github.com/tornadoweb/tornado/pull/2277 +Patch83: only-call-native_str-on-curl_debug-message-in-tornad.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65204 +Patch84: use-salt-call-from-salt-bundle-with-transactional_up.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65181 +Patch85: imporve-salt.utils.json.find_json-bsc-1213293.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/65266 +Patch86: fix-optimization_order-opt-to-prevent-test-fails.patch ### IMPORTANT: The line below is used as a snippet marker. Do not touch it. ### SALT PATCHES LIST END diff --git a/use-salt-call-from-salt-bundle-with-transactional_up.patch b/use-salt-call-from-salt-bundle-with-transactional_up.patch new file mode 100644 index 0000000..62f8a1c --- /dev/null +++ b/use-salt-call-from-salt-bundle-with-transactional_up.patch @@ -0,0 +1,103 @@ +From 0459d3f711eb9898f56a97d0bf0eb66fd1421a56 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 2 Oct 2023 13:25:52 +0200 +Subject: [PATCH] Use salt-call from salt bundle with + transactional_update + +* Use salt-call from the bundle with transactional_update + +* Add test checking which salt-call is selected by executable +--- + salt/modules/transactional_update.py | 13 +++++- + .../unit/modules/test_transactional_update.py | 44 +++++++++++++++++++ + 2 files changed, 56 insertions(+), 1 deletion(-) + +diff --git a/salt/modules/transactional_update.py b/salt/modules/transactional_update.py +index 658ebccc6b..d6915475f5 100644 +--- a/salt/modules/transactional_update.py ++++ b/salt/modules/transactional_update.py +@@ -276,6 +276,9 @@ transaction. + """ + + import logging ++import os.path ++import pathlib ++import sys + + import salt.client.ssh.state + import salt.client.ssh.wrapper.state +@@ -941,10 +944,18 @@ def call(function, *args, **kwargs): + activate_transaction = kwargs.pop("activate_transaction", False) + + try: ++ # Set default salt-call command ++ salt_call_cmd = "salt-call" ++ python_exec_dir = os.path.dirname(sys.executable) ++ if "venv-salt-minion" in pathlib.Path(python_exec_dir).parts: ++ # If the module is executed with the Salt Bundle, ++ # use salt-call from the Salt Bundle ++ salt_call_cmd = os.path.join(python_exec_dir, "salt-call") ++ + safe_kwargs = salt.utils.args.clean_kwargs(**kwargs) + salt_argv = ( + [ +- "salt-call", ++ salt_call_cmd, + "--out", + "json", + "-l", +diff --git a/tests/pytests/unit/modules/test_transactional_update.py b/tests/pytests/unit/modules/test_transactional_update.py +index 5d9294c49b..dbd72fd74b 100644 +--- a/tests/pytests/unit/modules/test_transactional_update.py ++++ b/tests/pytests/unit/modules/test_transactional_update.py +@@ -670,3 +670,47 @@ def test_single_queue_true(): + "salt.modules.transactional_update.call", MagicMock(return_value="result") + ): + assert tu.single("pkg.installed", name="emacs", queue=True) == "result" ++ ++ ++@pytest.mark.parametrize( ++ "executable,salt_call_cmd", ++ [ ++ ("/usr/bin/python3", "salt-call"), ++ ( ++ "/usr/lib/venv-salt-minion/bin/python", ++ "/usr/lib/venv-salt-minion/bin/salt-call", ++ ), ++ ], ++) ++def test_call_which_salt_call_selected_with_executable(executable, salt_call_cmd): ++ """Test transactional_update.chroot which salt-call used""" ++ utils_mock = { ++ "json.find_json": MagicMock(return_value={"return": "result"}), ++ } ++ salt_mock = { ++ "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}), ++ } ++ with patch("sys.executable", executable), patch.dict( ++ tu.__utils__, utils_mock ++ ), patch.dict(tu.__salt__, salt_mock): ++ assert tu.call("test.ping") == "result" ++ ++ salt_mock["cmd.run_all"].assert_called_with( ++ [ ++ "transactional-update", ++ "--non-interactive", ++ "--drop-if-no-change", ++ "--no-selfupdate", ++ "--continue", ++ "--quiet", ++ "run", ++ salt_call_cmd, ++ "--out", ++ "json", ++ "-l", ++ "quiet", ++ "--no-return-event", ++ "--", ++ "test.ping", ++ ] ++ ) +-- +2.42.0 +