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

openSUSE Build Service is sponsored by