File implement-the-calling-for-batch-async-from-the-salt-.patch of Package salt
From 9ed7cf072fab3292d9b4dc0a7a10b73fb3503f34 Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <vzhestkov@suse.com>
Date: Mon, 2 Oct 2023 13:24:15 +0200
Subject: [PATCH] Implement the calling for batch async from the salt
CLI
* Implement calling batch async with salt CLI
* Add the test for calling batch async with salt CLI
---
salt/cli/salt.py | 53 ++++++++++++++++++++++++++++-
tests/pytests/unit/cli/test_salt.py | 50 +++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 1 deletion(-)
create mode 100644 tests/pytests/unit/cli/test_salt.py
diff --git a/salt/cli/salt.py b/salt/cli/salt.py
index a474cfc85a..06311afcf0 100644
--- a/salt/cli/salt.py
+++ b/salt/cli/salt.py
@@ -47,7 +47,12 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser):
self.exit(2, f"{exc}\n")
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
@@ -293,6 +298,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.47.0