From 77005010dcb0b2e28bc521066071eecfe3aa9dcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?=
 <psuarezhernandez@suse.com>
Date: Mon, 15 Nov 2021 13:20:08 +0000
Subject: [PATCH] Refactor and improvements for "transactional-updates"
 module (#458)

Add --no-return-event option to salt-call

Act on concurrent flag when running highstate

Simplify transactional_update module to not use SSH wrapper

Fix tests for transactional update

Add changelog

Fix pylint issues

Fix failing unit test for state.highstate after refactor

Remove hack about tukit issue that has been already fixed
---
 changelog/61188.fixed                         |   3 +
 salt/cli/caller.py                            |   2 +-
 salt/modules/state.py                         |  12 +-
 salt/modules/transactional_update.py          | 235 +----
 salt/utils/parsers.py                         |   9 +-
 .../unit/modules/test_transactional_update.py | 667 ++++++++++++
 tests/unit/modules/test_state.py              |   2 +-
 .../unit/modules/test_transactional_update.py | 947 ------------------
 8 files changed, 720 insertions(+), 1157 deletions(-)
 create mode 100644 changelog/61188.fixed
 create mode 100644 tests/pytests/unit/modules/test_transactional_update.py
 delete mode 100644 tests/unit/modules/test_transactional_update.py

diff --git a/changelog/61188.fixed b/changelog/61188.fixed
new file mode 100644
index 0000000000..102a8982a6
--- /dev/null
+++ b/changelog/61188.fixed
@@ -0,0 +1,3 @@
+Add "--no-return-event" option to salt-call to prevent sending return event back to master.
+Make "state.highstate" to acts on concurrent flag.
+Simplify "transactional_update" module to not use SSH wrapper and allow more flexible execution
diff --git a/salt/cli/caller.py b/salt/cli/caller.py
index b755fb4801..4d50d56e97 100644
--- a/salt/cli/caller.py
+++ b/salt/cli/caller.py
@@ -287,7 +287,7 @@ class BaseCaller(object):
                 pass
 
         # return the job infos back up to the respective minion's master
-        if not is_local:
+        if not is_local and not self.opts.get("no_return_event", False):
             try:
                 mret = ret.copy()
                 mret['jid'] = 'req'
diff --git a/salt/modules/state.py b/salt/modules/state.py
index a1effd884e..111af3bf07 100644
--- a/salt/modules/state.py
+++ b/salt/modules/state.py
@@ -1025,9 +1025,15 @@ def highstate(test=None, queue=False, **kwargs):
         }
         return ret
 
-    conflict = _check_queue(queue, kwargs)
-    if conflict is not None:
-        return conflict
+    concurrent = kwargs.get("concurrent", False)
+
+    if queue:
+        _wait(kwargs.get("__pub_jid"))
+    else:
+        conflict = running(concurrent)
+        if conflict:
+            __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR
+            return conflict
 
     orig_test = __opts__.get('test', None)
     opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
diff --git a/salt/modules/transactional_update.py b/salt/modules/transactional_update.py
index 3af9d91822..0ce0bf2f2c 100644
--- a/salt/modules/transactional_update.py
+++ b/salt/modules/transactional_update.py
@@ -275,11 +275,7 @@ transaction.
 
 """
 
-import copy
 import logging
-import os
-import sys
-import tempfile
 
 # required by _check_queue invocation later
 import time  # pylint: disable=unused-import
@@ -312,11 +308,6 @@ def __virtual__():
         return (False, "Module transactional_update requires a transactional system")
 
 
-class TransactionalUpdateHighstate(salt.client.ssh.state.SSHHighState):
-    def _master_tops(self):
-        return self.client.master_tops()
-
-
 def _global_params(self_update, snapshot=None, quiet=False):
     """Utility function to prepare common global parameters."""
     params = ["--non-interactive", "--drop-if-no-change"]
@@ -950,65 +941,42 @@ def call(function, *args, **kwargs):
 
     activate_transaction = kwargs.pop("activate_transaction", False)
 
-    # Generate the salt-thin and create a temporary directory in a
-    # place that the new transaction will have access to, and where we
-    # can untar salt-thin
-    thin_path = __utils__["thin.gen_thin"](
-        __opts__["cachedir"],
-        extra_mods=__salt__["config.option"]("thin_extra_mods", ""),
-        so_mods=__salt__["config.option"]("thin_so_mods", ""),
-    )
-    thin_dest_path = tempfile.mkdtemp(dir=__opts__["cachedir"])
-    # Some bug in Salt is preventing us to use `archive.tar` here. A
-    # AsyncZeroMQReqChannel is not closed at the end of the salt-call,
-    # and makes the client never exit.
-    #
-    # stdout = __salt__['archive.tar']('xzf', thin_path, dest=thin_dest_path)
-    #
-    stdout = __salt__["cmd.run"](["tar", "xzf", thin_path, "-C", thin_dest_path])
-    if stdout:
-        __utils__["files.rm_rf"](thin_dest_path)
-        return {"result": False, "comment": stdout}
-
     try:
         safe_kwargs = salt.utils.args.clean_kwargs(**kwargs)
         salt_argv = (
             [
-                "python{}".format(sys.version_info[0]),
-                os.path.join(thin_dest_path, "salt-call"),
-                "--metadata",
-                "--local",
-                "--log-file",
-                os.path.join(thin_dest_path, "log"),
-                "--cachedir",
-                os.path.join(thin_dest_path, "cache"),
+                "salt-call",
                 "--out",
                 "json",
                 "-l",
                 "quiet",
+                "--no-return-event",
                 "--",
                 function,
             ]
             + list(args)
             + ["{}={}".format(k, v) for (k, v) in safe_kwargs.items()]
         )
+
         try:
             ret_stdout = run([str(x) for x in salt_argv], snapshot="continue")
         except salt.exceptions.CommandExecutionError as e:
+            # This happens when there was an problem with salt-call execution
             ret_stdout = e.message
 
         # Process "real" result in stdout
         try:
             data = __utils__["json.find_json"](ret_stdout)
             local = data.get("local", data)
-            if isinstance(local, dict) and "retcode" in local:
-                __context__["retcode"] = local["retcode"]
-            return local.get("return", data)
+            if isinstance(local, dict):
+                if "retcode" in local:
+                    __context__["retcode"] = local["retcode"]
+                return local.get("return", local)
+            else:
+                return local
         except ValueError:
             return {"result": False, "retcode": 1, "comment": ret_stdout}
     finally:
-        __utils__["files.rm_rf"](thin_dest_path)
-
         # Check if reboot is needed
         if activate_transaction and pending_transaction():
             reboot()
@@ -1044,49 +1012,7 @@ def apply_(mods=None, **kwargs):
     return highstate(**kwargs)
 
 
-def _create_and_execute_salt_state(
-    chunks, file_refs, test, hash_type, activate_transaction
-):
-    """Create the salt_state tarball, and execute it in a transaction"""
-
-    # Create the tar containing the state pkg and relevant files.
-    salt.client.ssh.wrapper.state._cleanup_slsmod_low_data(chunks)
-    trans_tar = salt.client.ssh.state.prep_trans_tar(
-        salt.fileclient.get_file_client(__opts__), chunks, file_refs, __pillar__
-    )
-    trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, hash_type)
-
-    ret = None
-
-    # Create a temporary directory accesible later by the transaction
-    # where we can move the salt_state.tgz
-    salt_state_path = tempfile.mkdtemp(dir=__opts__["cachedir"])
-    salt_state_path = os.path.join(salt_state_path, "salt_state.tgz")
-    try:
-        salt.utils.files.copyfile(trans_tar, salt_state_path)
-        ret = call(
-            "state.pkg",
-            salt_state_path,
-            test=test,
-            pkg_sum=trans_tar_sum,
-            hash_type=hash_type,
-            activate_transaction=activate_transaction,
-        )
-    finally:
-        __utils__["files.rm_rf"](salt_state_path)
-
-    return ret
-
-
-def sls(
-    mods,
-    saltenv="base",
-    test=None,
-    exclude=None,
-    activate_transaction=False,
-    queue=False,
-    **kwargs
-):
+def sls(mods, activate_transaction=False, queue=False, **kwargs):
     """Execute the states in one or more SLS files inside a transaction.
 
     saltenv
@@ -1132,55 +1058,14 @@ def sls(
     if conflict is not None:
         return conflict
 
-    # Get a copy of the pillar data, to avoid overwriting the current
-    # pillar, instead the one delegated
-    pillar = copy.deepcopy(__pillar__)
-    pillar.update(kwargs.get("pillar", {}))
-
-    # Clone the options data and apply some default values. May not be
-    # needed, as this module just delegate
-    opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
-    st_ = TransactionalUpdateHighstate(
-        opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__)
-    )
-
-    if isinstance(mods, str):
-        mods = mods.split(",")
-
-    high_data, errors = st_.render_highstate({saltenv: mods})
-    if exclude:
-        if isinstance(exclude, str):
-            exclude = exclude.split(",")
-        if "__exclude__" in high_data:
-            high_data["__exclude__"].extend(exclude)
-        else:
-            high_data["__exclude__"] = exclude
-
-    high_data, ext_errors = st_.state.reconcile_extend(high_data)
-    errors += ext_errors
-    errors += st_.state.verify_high(high_data)
-    if errors:
-        return errors
-
-    high_data, req_in_errors = st_.state.requisite_in(high_data)
-    errors += req_in_errors
-    if errors:
-        return errors
-
-    high_data = st_.state.apply_exclude(high_data)
-
-    # Compile and verify the raw chunks
-    chunks = st_.state.compile_high_data(high_data)
-    file_refs = salt.client.ssh.state.lowstate_file_refs(
-        chunks,
-        salt.client.ssh.wrapper.state._merge_extra_filerefs(
-            kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "")
-        ),
-    )
+    concurrent = kwargs.pop("concurrent", True)
 
-    hash_type = opts["hash_type"]
-    return _create_and_execute_salt_state(
-        chunks, file_refs, test, hash_type, activate_transaction
+    return call(
+        "state.sls",
+        mods,
+        activate_transaction=activate_transaction,
+        concurrent=concurrent,
+        **kwargs
     )
 
 
@@ -1216,40 +1101,15 @@ def highstate(activate_transaction=False, queue=False, **kwargs):
     if conflict is not None:
         return conflict
 
-    # Get a copy of the pillar data, to avoid overwriting the current
-    # pillar, instead the one delegated
-    pillar = copy.deepcopy(__pillar__)
-    pillar.update(kwargs.get("pillar", {}))
-
-    # Clone the options data and apply some default values. May not be
-    # needed, as this module just delegate
-    opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
-    st_ = TransactionalUpdateHighstate(
-        opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__)
-    )
-
-    # Compile and verify the raw chunks
-    chunks = st_.compile_low_chunks()
-    file_refs = salt.client.ssh.state.lowstate_file_refs(
-        chunks,
-        salt.client.ssh.wrapper.state._merge_extra_filerefs(
-            kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "")
-        ),
-    )
-    # Check for errors
-    for chunk in chunks:
-        if not isinstance(chunk, dict):
-            __context__["retcode"] = 1
-            return chunks
-
-    test = kwargs.pop("test", False)
-    hash_type = opts["hash_type"]
-    return _create_and_execute_salt_state(
-        chunks, file_refs, test, hash_type, activate_transaction
+    return call(
+        "state.highstate",
+        activate_transaction=activate_transaction,
+        concurrent=True,
+        **kwargs
     )
 
 
-def single(fun, name, test=None, activate_transaction=False, queue=False, **kwargs):
+def single(fun, name, activate_transaction=False, queue=False, **kwargs):
     """Execute a single state function with the named kwargs, returns
     False if insufficient data is sent to the command
 
@@ -1282,44 +1142,11 @@ def single(fun, name, test=None, activate_transaction=False, queue=False, **kwar
     if conflict is not None:
         return conflict
 
-    # Get a copy of the pillar data, to avoid overwriting the current
-    # pillar, instead the one delegated
-    pillar = copy.deepcopy(__pillar__)
-    pillar.update(kwargs.get("pillar", {}))
-
-    # Clone the options data and apply some default values. May not be
-    # needed, as this module just delegate
-    opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
-    st_ = salt.client.ssh.state.SSHState(opts, pillar)
-
-    # state.fun -> [state, fun]
-    comps = fun.split(".")
-    if len(comps) < 2:
-        __context__["retcode"] = 1
-        return "Invalid function passed"
-
-    # Create the low chunk, using kwargs as a base
-    kwargs.update({"state": comps[0], "fun": comps[1], "__id__": name, "name": name})
-
-    # Verify the low chunk
-    err = st_.verify_data(kwargs)
-    if err:
-        __context__["retcode"] = 1
-        return err
-
-    # Must be a list of low-chunks
-    chunks = [kwargs]
-
-    # Retrieve file refs for the state run, so we can copy relevant
-    # files down to the minion before executing the state
-    file_refs = salt.client.ssh.state.lowstate_file_refs(
-        chunks,
-        salt.client.ssh.wrapper.state._merge_extra_filerefs(
-            kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "")
-        ),
-    )
-
-    hash_type = opts["hash_type"]
-    return _create_and_execute_salt_state(
-        chunks, file_refs, test, hash_type, activate_transaction
+    return call(
+        "state.single",
+        fun=fun,
+        name=name,
+        activate_transaction=activate_transaction,
+        concurrent=True,
+        **kwargs
     )
diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py
index e701b2b53d..17d04ab43c 100644
--- a/salt/utils/parsers.py
+++ b/salt/utils/parsers.py
@@ -2795,7 +2795,14 @@ class SaltCallOptionParser(six.with_metaclass(OptionParserMeta,
             help=('Force a refresh of the grains cache.')
         )
         self.add_option(
-            '-t', '--timeout',
+            "--no-return-event",
+            default=False,
+            action="store_true",
+            help=("Do not produce the return event back to master."),
+        )
+        self.add_option(
+            "-t",
+            "--timeout",
             default=60,
             dest='auth_timeout',
             type=int,
diff --git a/tests/pytests/unit/modules/test_transactional_update.py b/tests/pytests/unit/modules/test_transactional_update.py
new file mode 100644
index 0000000000..8cb96e3505
--- /dev/null
+++ b/tests/pytests/unit/modules/test_transactional_update.py
@@ -0,0 +1,667 @@
+import pytest
+import salt.modules.state as statemod
+import salt.modules.transactional_update as tu
+from salt.exceptions import CommandExecutionError
+from tests.support.mock import MagicMock, patch
+
+pytestmark = [
+    pytest.mark.skip_on_windows(reason="Not supported on Windows"),
+]
+
+
+@pytest.fixture
+def configure_loader_modules():
+    return {
+        tu: {
+            "__salt__": {},
+            "__utils__": {"files.rm_rf": MagicMock()},
+            "__pillar__": {},
+            "__opts__": {"extension_modules": "", "cachedir": "/tmp/"},
+        },
+        statemod: {"__salt__": {}, "__context__": {}},
+    }
+
+
+def test__global_params_no_self_update():
+    """Test transactional_update._global_params without self_update"""
+    assert tu._global_params(self_update=False) == [
+        "--non-interactive",
+        "--drop-if-no-change",
+        "--no-selfupdate",
+    ]
+
+
+def test__global_params_self_update():
+    """Test transactional_update._global_params with self_update"""
+    assert tu._global_params(self_update=True) == [
+        "--non-interactive",
+        "--drop-if-no-change",
+    ]
+
+
+def test__global_params_no_self_update_snapshot():
+    """Test transactional_update._global_params without self_update and
+    snapshot
+
+    """
+    assert tu._global_params(self_update=False, snapshot=10) == [
+        "--non-interactive",
+        "--drop-if-no-change",
+        "--no-selfupdate",
+        "--continue",
+        10,
+    ]
+
+
+def test__global_params_no_self_update_continue():
+    """Test transactional_update._global_params without self_update and
+    snapshot conitue
+
+    """
+    assert tu._global_params(self_update=False, snapshot="continue") == [
+        "--non-interactive",
+        "--drop-if-no-change",
+        "--no-selfupdate",
+        "--continue",
+    ]
+
+
+def test__pkg_params_no_packages():
+    """Test transactional_update._pkg_params without packages"""
+    with pytest.raises(CommandExecutionError):
+        tu._pkg_params(pkg=None, pkgs=None, args=None)
+
+
+def test__pkg_params_pkg():
+    """Test transactional_update._pkg_params with single package"""
+    assert tu._pkg_params(pkg="pkg1", pkgs=None, args=None) == ["pkg1"]
+
+
+def test__pkg_params_pkgs():
+    """Test transactional_update._pkg_params with packages"""
+    assert tu._pkg_params(pkg=None, pkgs="pkg1", args=None) == ["pkg1"]
+    assert tu._pkg_params(pkg=None, pkgs="pkg1 pkg2 ", args=None) == [
+        "pkg1",
+        "pkg2",
+    ]
+    assert tu._pkg_params(pkg=None, pkgs=["pkg1", "pkg2"], args=None) == [
+        "pkg1",
+        "pkg2",
+    ]
+
+
+def test__pkg_params_pkg_pkgs():
+    """Test transactional_update._pkg_params with packages"""
+    assert tu._pkg_params(pkg="pkg1", pkgs="pkg2", args=None) == [
+        "pkg1",
+        "pkg2",
+    ]
+
+
+def test__pkg_params_args():
+    """Test transactional_update._pkg_params with argumens"""
+    assert tu._pkg_params(pkg="pkg1", pkgs=None, args="--arg1") == [
+        "--arg1",
+        "pkg1",
+    ]
+    assert tu._pkg_params(pkg="pkg1", pkgs=None, args="--arg1 --arg2") == [
+        "--arg1",
+        "--arg2",
+        "pkg1",
+    ]
+    assert tu._pkg_params(pkg="pkg1", pkgs=None, args=["--arg1", "--arg2"]) == [
+        "--arg1",
+        "--arg2",
+        "pkg1",
+    ]
+
+
+def test_transactional_transactional():
+    """Test transactional_update.transactional"""
+    matrix = (("/usr/sbin/transactional-update", True), ("", False))
+
+    for path_which, result in matrix:
+        utils_mock = {"path.which": MagicMock(return_value=path_which)}
+
+        with patch.dict(tu.__utils__, utils_mock):
+            assert tu.transactional() is result
+            utils_mock["path.which"].assert_called_with("transactional-update")
+
+
+def test_in_transaction():
+    """Test transactional_update.in_transaction"""
+    matrix = (
+        ("/usr/sbin/transactional-update", True, True),
+        ("/usr/sbin/transactional-update", False, False),
+        ("", True, False),
+        ("", False, False),
+    )
+
+    for path_which, in_chroot, result in matrix:
+        utils_mock = {"path.which": MagicMock(return_value=path_which)}
+        salt_mock = {"chroot.in_chroot": MagicMock(return_value=in_chroot)}
+
+        with patch.dict(tu.__utils__, utils_mock):
+            with patch.dict(tu.__salt__, salt_mock):
+                assert tu.in_transaction() is result
+
+
+def test_commands_with_global_params():
+    """Test commands that only accept global params"""
+    for cmd in [
+        "cleanup",
+        "cleanup_snapshots",
+        "cleanup_overlays",
+        "grub_cfg",
+        "bootloader",
+        "initrd",
+        "kdump",
+        "reboot",
+        "dup",
+        "up",
+        "patch",
+        "migration",
+    ]:
+        salt_mock = {
+            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+        }
+        with patch.dict(tu.__salt__, salt_mock):
+            assert getattr(tu, cmd)() == "output"
+            salt_mock["cmd.run_all"].assert_called_with(
+                [
+                    "transactional-update",
+                    "--non-interactive",
+                    "--drop-if-no-change",
+                    "--no-selfupdate",
+                    cmd.replace("_", ".")
+                    if cmd.startswith("grub")
+                    else cmd.replace("_", "-"),
+                ]
+            )
+
+
+def test_run_error():
+    """Test transactional_update.run with missing command"""
+    with pytest.raises(CommandExecutionError):
+        tu.run(None)
+
+
+def test_run_string():
+    """Test transactional_update.run with command as string"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.run("cmd --flag p1 p2") == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            [
+                "transactional-update",
+                "--non-interactive",
+                "--drop-if-no-change",
+                "--no-selfupdate",
+                "--quiet",
+                "run",
+                "cmd",
+                "--flag",
+                "p1",
+                "p2",
+            ]
+        )
+
+
+def test_run_array():
+    """Test transactional_update.run with command as array"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.run(["cmd", "--flag", "p1", "p2"]) == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            [
+                "transactional-update",
+                "--non-interactive",
+                "--drop-if-no-change",
+                "--no-selfupdate",
+                "--quiet",
+                "run",
+                "cmd",
+                "--flag",
+                "p1",
+                "p2",
+            ]
+        )
+
+
+def test_pkg_commands():
+    """Test transactional_update.pkg_* commands"""
+    for cmd in ["pkg_install", "pkg_remove", "pkg_update"]:
+        salt_mock = {
+            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+        }
+        with patch.dict(tu.__salt__, salt_mock):
+            assert getattr(tu, cmd)("pkg1", "pkg2 pkg3", "--arg") == "output"
+            salt_mock["cmd.run_all"].assert_called_with(
+                [
+                    "transactional-update",
+                    "--non-interactive",
+                    "--drop-if-no-change",
+                    "--no-selfupdate",
+                    "pkg",
+                    cmd.replace("pkg_", ""),
+                    "--arg",
+                    "pkg1",
+                    "pkg2",
+                    "pkg3",
+                ]
+            )
+
+
+def test_rollback_error():
+    """Test transactional_update.rollback with wrong snapshot"""
+    with pytest.raises(CommandExecutionError):
+        tu.rollback("error")
+
+
+def test_rollback_default():
+    """Test transactional_update.rollback with default snapshot"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.rollback() == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            ["transactional-update", "rollback"]
+        )
+
+
+def test_rollback_snapshot_number():
+    """Test transactional_update.rollback with numeric snapshot"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.rollback(10) == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            ["transactional-update", "rollback", 10]
+        )
+
+
+def test_rollback_snapshot_str():
+    """Test transactional_update.rollback with string snapshot"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.rollback("10") == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            ["transactional-update", "rollback", "10"]
+        )
+
+
+def test_rollback_last():
+    """Test transactional_update.rollback with last snapshot"""
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
+    }
+    with patch.dict(tu.__salt__, salt_mock):
+        assert tu.rollback("last") == "output"
+        salt_mock["cmd.run_all"].assert_called_with(
+            ["transactional-update", "rollback", "last"]
+        )
+
+
+def test_pending_transaction():
+    """Test transactional_update.pending_transaction"""
+    matrix = (
+        (False, ["1", "2+", "3-"], True),
+        (False, ["1", "2-", "3+"], True),
+        (False, ["1", "2", "3*"], False),
+    )
+
+    for in_transaction, snapshots, result in matrix:
+        salt_mock = {
+            "cmd.run_all": MagicMock(return_value={"stdout": snapshots, "retcode": 0})
+        }
+
+        tu_in_transaction = "salt.modules.transactional_update.in_transaction"
+        with patch(tu_in_transaction) as in_transaction_mock:
+            in_transaction_mock.return_value = in_transaction
+            with patch.dict(tu.__salt__, salt_mock):
+                assert tu.pending_transaction() is result
+                salt_mock["cmd.run_all"].assert_called_with(
+                    ["snapper", "--no-dbus", "list", "--columns", "number"]
+                )
+
+
+def test_pending_transaction_in_transaction():
+    """Test transactional_update.pending_transaction when in transaction"""
+    tu_in_transaction = "salt.modules.transactional_update.in_transaction"
+    with patch(tu_in_transaction) as in_transaction_mock:
+        in_transaction_mock.return_value = True
+        with pytest.raises(CommandExecutionError):
+            tu.pending_transaction()
+
+
+def test_call_fails_input_validation():
+    """Test transactional_update.call missing function name"""
+    with pytest.raises(CommandExecutionError):
+        tu.call("")
+
+
+def test_call_fails_function():
+    """Test transactional_update.chroot when fails the function"""
+    utils_mock = {
+        "json.find_json": MagicMock(side_effect=ValueError()),
+    }
+    salt_mock = {
+        "cmd.run_all": MagicMock(
+            return_value={"retcode": 0, "stdout": "Not found", "stderr": ""}
+        ),
+    }
+    with patch.dict(tu.__utils__, utils_mock), patch.dict(tu.__salt__, salt_mock):
+        assert tu.call("test.ping") == {
+            "result": False,
+            "retcode": 1,
+            "comment": "Not found",
+        }
+
+        salt_mock["cmd.run_all"].assert_called_with(
+            [
+                "transactional-update",
+                "--non-interactive",
+                "--drop-if-no-change",
+                "--no-selfupdate",
+                "--continue",
+                "--quiet",
+                "run",
+                "salt-call",
+                "--out",
+                "json",
+                "-l",
+                "quiet",
+                "--no-return-event",
+                "--",
+                "test.ping",
+            ]
+        )
+
+
+def test_call_success_no_reboot():
+    """Test transactional_update.chroot when succeed"""
+    utils_mock = {
+        "json.find_json": MagicMock(return_value={"return": "result"}),
+    }
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
+    }
+    with 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",
+                "--out",
+                "json",
+                "-l",
+                "quiet",
+                "--no-return-event",
+                "--",
+                "test.ping",
+            ]
+        )
+
+
+def test_call_success_reboot():
+    """Test transactional_update.chroot when succeed and reboot"""
+    pending_transaction_mock = MagicMock(return_value=True)
+    reboot_mock = MagicMock()
+    utils_mock = {
+        "json.find_json": MagicMock(return_value={"return": "result"}),
+    }
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
+    }
+    with patch.dict(tu.__utils__, utils_mock), patch.dict(
+        tu.__salt__, salt_mock
+    ), patch.dict(tu.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.pending_transaction",
+        pending_transaction_mock,
+    ), patch(
+        "salt.modules.transactional_update.reboot", reboot_mock
+    ):
+        assert (
+            tu.call("transactional_update.dup", activate_transaction=True) == "result"
+        )
+
+        salt_mock["cmd.run_all"].assert_called_with(
+            [
+                "transactional-update",
+                "--non-interactive",
+                "--drop-if-no-change",
+                "--no-selfupdate",
+                "--continue",
+                "--quiet",
+                "run",
+                "salt-call",
+                "--out",
+                "json",
+                "-l",
+                "quiet",
+                "--no-return-event",
+                "--",
+                "transactional_update.dup",
+            ]
+        )
+        pending_transaction_mock.assert_called_once()
+        reboot_mock.assert_called_once()
+
+
+def test_call_success_parameters():
+    """Test transactional_update.chroot when succeed with parameters"""
+    utils_mock = {
+        "json.find_json": MagicMock(return_value={"return": "result"}),
+    }
+    salt_mock = {
+        "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
+    }
+    with patch.dict(tu.__utils__, utils_mock), patch.dict(tu.__salt__, salt_mock):
+        assert tu.call("module.function", key="value") == "result"
+
+        salt_mock["cmd.run_all"].assert_called_with(
+            [
+                "transactional-update",
+                "--non-interactive",
+                "--drop-if-no-change",
+                "--no-selfupdate",
+                "--continue",
+                "--quiet",
+                "run",
+                "salt-call",
+                "--out",
+                "json",
+                "-l",
+                "quiet",
+                "--no-return-event",
+                "--",
+                "module.function",
+                "key=value",
+            ]
+        )
+
+
+def test_sls():
+    """Test transactional_update.sls"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(return_value=[]),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.sls("module") == "result"
+
+
+def test_sls_queue_true():
+    """Test transactional_update.sls"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.sls("module", queue=True) == "result"
+
+
+def test_sls_queue_false_failing():
+    """Test transactional_update.sls"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.sls("module", queue=False) == [
+            'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
+        ]
+
+
+def test_highstate():
+    """Test transactional_update.highstage"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(return_value=[]),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.highstate() == "result"
+
+
+def test_highstate_queue_true():
+    """Test transactional_update.highstage"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.highstate(queue=True) == "result"
+
+
+def test_highstate_queue_false_failing():
+    """Test transactional_update.highstage"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.highstate(queue=False) == [
+            'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
+        ]
+
+
+def test_single():
+    """Test transactional_update.single"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(return_value=[]),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.single("pkg.installed", name="emacs") == "result"
+
+
+def test_single_queue_false_failing():
+    """Test transactional_update.single"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.single("pkg.installed", name="emacs", queue=False) == [
+            'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
+        ]
+
+
+def test_single_queue_true():
+    """Test transactional_update.single"""
+    salt_mock = {
+        "saltutil.is_running": MagicMock(
+            side_effect=[
+                [
+                    {
+                        "fun": "state.running",
+                        "pid": "4126",
+                        "jid": "20150325123407204096",
+                    }
+                ],
+                [],
+            ]
+        ),
+    }
+    with patch.dict(statemod.__salt__, salt_mock), patch(
+        "salt.modules.transactional_update.call", MagicMock(return_value="result")
+    ):
+        assert tu.single("pkg.installed", name="emacs", queue=True) == "result"
diff --git a/tests/unit/modules/test_state.py b/tests/unit/modules/test_state.py
index cda846595e..f19eafa6e5 100644
--- a/tests/unit/modules/test_state.py
+++ b/tests/unit/modules/test_state.py
@@ -783,7 +783,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
                                   'result': 'False'})
 
             mock = MagicMock(side_effect=["A", None, None])
-            with patch.object(state, '_check_queue', mock):
+            with patch.object(state, "running", mock):
                 self.assertEqual(state.highstate("whitelist=sls1.sls"), "A")
 
                 with patch.dict(state.__opts__, {"test": "A"}):
diff --git a/tests/unit/modules/test_transactional_update.py b/tests/unit/modules/test_transactional_update.py
deleted file mode 100644
index 1776d4a10b..0000000000
--- a/tests/unit/modules/test_transactional_update.py
+++ /dev/null
@@ -1,947 +0,0 @@
-import sys
-
-import pytest
-import salt.modules.state as statemod
-import salt.modules.transactional_update as tu
-import salt.utils.platform
-from salt.exceptions import CommandExecutionError
-
-# Import Salt Testing Libs
-from tests.support.mixins import LoaderModuleMockMixin
-from tests.support.mock import MagicMock, patch
-from tests.support.unit import TestCase, skipIf
-
-
-@skipIf(salt.utils.platform.is_windows(), "Do not run these tests on Windows")
-class TransactionalUpdateTestCase(TestCase, LoaderModuleMockMixin):
-    """
-    Test cases for salt.modules.transactional_update
-    """
-
-    def setup_loader_modules(self):
-        return {
-            tu: {"__salt__": {}, "__utils__": {}},
-            statemod: {"__salt__": {}, "__context__": {}},
-        }
-
-    def test__global_params_no_self_update(self):
-        """Test transactional_update._global_params without self_update"""
-        assert tu._global_params(self_update=False) == [
-            "--non-interactive",
-            "--drop-if-no-change",
-            "--no-selfupdate",
-        ]
-
-    def test__global_params_self_update(self):
-        """Test transactional_update._global_params with self_update"""
-        assert tu._global_params(self_update=True) == [
-            "--non-interactive",
-            "--drop-if-no-change",
-        ]
-
-    def test__global_params_no_self_update_snapshot(self):
-        """Test transactional_update._global_params without self_update and
-        snapshot
-
-        """
-        assert tu._global_params(self_update=False, snapshot=10) == [
-            "--non-interactive",
-            "--drop-if-no-change",
-            "--no-selfupdate",
-            "--continue",
-            10,
-        ]
-
-    def test__global_params_no_self_update_continue(self):
-        """Test transactional_update._global_params without self_update and
-        snapshot conitue
-
-        """
-        assert tu._global_params(self_update=False, snapshot="continue") == [
-            "--non-interactive",
-            "--drop-if-no-change",
-            "--no-selfupdate",
-            "--continue",
-        ]
-
-    def test__pkg_params_no_packages(self):
-        """Test transactional_update._pkg_params without packages"""
-        with pytest.raises(CommandExecutionError):
-            tu._pkg_params(pkg=None, pkgs=None, args=None)
-
-    def test__pkg_params_pkg(self):
-        """Test transactional_update._pkg_params with single package"""
-        assert tu._pkg_params(pkg="pkg1", pkgs=None, args=None) == ["pkg1"]
-
-    def test__pkg_params_pkgs(self):
-        """Test transactional_update._pkg_params with packages"""
-        assert tu._pkg_params(pkg=None, pkgs="pkg1", args=None) == ["pkg1"]
-        assert tu._pkg_params(pkg=None, pkgs="pkg1 pkg2 ", args=None) == [
-            "pkg1",
-            "pkg2",
-        ]
-        assert tu._pkg_params(pkg=None, pkgs=["pkg1", "pkg2"], args=None) == [
-            "pkg1",
-            "pkg2",
-        ]
-
-    def test__pkg_params_pkg_pkgs(self):
-        """Test transactional_update._pkg_params with packages"""
-        assert tu._pkg_params(pkg="pkg1", pkgs="pkg2", args=None) == [
-            "pkg1",
-            "pkg2",
-        ]
-
-    def test__pkg_params_args(self):
-        """Test transactional_update._pkg_params with argumens"""
-        assert tu._pkg_params(pkg="pkg1", pkgs=None, args="--arg1") == [
-            "--arg1",
-            "pkg1",
-        ]
-        assert tu._pkg_params(pkg="pkg1", pkgs=None, args="--arg1 --arg2") == [
-            "--arg1",
-            "--arg2",
-            "pkg1",
-        ]
-        assert tu._pkg_params(pkg="pkg1", pkgs=None, args=["--arg1", "--arg2"]) == [
-            "--arg1",
-            "--arg2",
-            "pkg1",
-        ]
-
-    def test_transactional_transactional(self):
-        """Test transactional_update.transactional"""
-        matrix = (("/usr/sbin/transactional-update", True), ("", False))
-
-        for path_which, result in matrix:
-            utils_mock = {"path.which": MagicMock(return_value=path_which)}
-
-            with patch.dict(tu.__utils__, utils_mock):
-                assert tu.transactional() is result
-                utils_mock["path.which"].assert_called_with("transactional-update")
-
-    def test_in_transaction(self):
-        """Test transactional_update.in_transaction"""
-        matrix = (
-            ("/usr/sbin/transactional-update", True, True),
-            ("/usr/sbin/transactional-update", False, False),
-            ("", True, False),
-            ("", False, False),
-        )
-
-        for path_which, in_chroot, result in matrix:
-            utils_mock = {"path.which": MagicMock(return_value=path_which)}
-            salt_mock = {"chroot.in_chroot": MagicMock(return_value=in_chroot)}
-
-            with patch.dict(tu.__utils__, utils_mock):
-                with patch.dict(tu.__salt__, salt_mock):
-                    assert tu.in_transaction() is result
-
-    def test_commands_with_global_params(self):
-        """Test commands that only accept global params"""
-        for cmd in [
-            "cleanup",
-            "cleanup_snapshots",
-            "cleanup_overlays",
-            "grub_cfg",
-            "bootloader",
-            "initrd",
-            "kdump",
-            "reboot",
-            "dup",
-            "up",
-            "patch",
-            "migration",
-        ]:
-            salt_mock = {
-                "cmd.run_all": MagicMock(
-                    return_value={"stdout": "output", "retcode": 0}
-                )
-            }
-            with patch.dict(tu.__salt__, salt_mock):
-                assert getattr(tu, cmd)() == "output"
-                salt_mock["cmd.run_all"].assert_called_with(
-                    [
-                        "transactional-update",
-                        "--non-interactive",
-                        "--drop-if-no-change",
-                        "--no-selfupdate",
-                        cmd.replace("_", ".")
-                        if cmd.startswith("grub")
-                        else cmd.replace("_", "-"),
-                    ]
-                )
-
-    def test_run_error(self):
-        """Test transactional_update.run with missing command"""
-        with pytest.raises(CommandExecutionError):
-            tu.run(None)
-
-    def test_run_string(self):
-        """Test transactional_update.run with command as string"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.run("cmd --flag p1 p2") == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--quiet",
-                    "run",
-                    "cmd",
-                    "--flag",
-                    "p1",
-                    "p2",
-                ]
-            )
-
-    def test_run_array(self):
-        """Test transactional_update.run with command as array"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.run(["cmd", "--flag", "p1", "p2"]) == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--quiet",
-                    "run",
-                    "cmd",
-                    "--flag",
-                    "p1",
-                    "p2",
-                ]
-            )
-
-    def test_pkg_commands(self):
-        """Test transactional_update.pkg_* commands"""
-        for cmd in ["pkg_install", "pkg_remove", "pkg_update"]:
-            salt_mock = {
-                "cmd.run_all": MagicMock(
-                    return_value={"stdout": "output", "retcode": 0}
-                )
-            }
-            with patch.dict(tu.__salt__, salt_mock):
-                assert getattr(tu, cmd)("pkg1", "pkg2 pkg3", "--arg") == "output"
-                salt_mock["cmd.run_all"].assert_called_with(
-                    [
-                        "transactional-update",
-                        "--non-interactive",
-                        "--drop-if-no-change",
-                        "--no-selfupdate",
-                        "pkg",
-                        cmd.replace("pkg_", ""),
-                        "--arg",
-                        "pkg1",
-                        "pkg2",
-                        "pkg3",
-                    ]
-                )
-
-    def test_rollback_error(self):
-        """Test transactional_update.rollback with wrong snapshot"""
-        with pytest.raises(CommandExecutionError):
-            tu.rollback("error")
-
-    def test_rollback_default(self):
-        """Test transactional_update.rollback with default snapshot"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.rollback() == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                ["transactional-update", "rollback"]
-            )
-
-    def test_rollback_snapshot_number(self):
-        """Test transactional_update.rollback with numeric snapshot"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.rollback(10) == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                ["transactional-update", "rollback", 10]
-            )
-
-    def test_rollback_snapshot_str(self):
-        """Test transactional_update.rollback with string snapshot"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.rollback("10") == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                ["transactional-update", "rollback", "10"]
-            )
-
-    def test_rollback_last(self):
-        """Test transactional_update.rollback with last snapshot"""
-        salt_mock = {
-            "cmd.run_all": MagicMock(return_value={"stdout": "output", "retcode": 0})
-        }
-        with patch.dict(tu.__salt__, salt_mock):
-            assert tu.rollback("last") == "output"
-            salt_mock["cmd.run_all"].assert_called_with(
-                ["transactional-update", "rollback", "last"]
-            )
-
-    def test_pending_transaction(self):
-        """Test transactional_update.pending_transaction"""
-        matrix = (
-            (False, ["1", "2+", "3-"], True),
-            (False, ["1", "2-", "3+"], True),
-            (False, ["1", "2", "3*"], False),
-        )
-
-        for in_transaction, snapshots, result in matrix:
-            salt_mock = {
-                "cmd.run_all": MagicMock(
-                    return_value={"stdout": snapshots, "retcode": 0}
-                )
-            }
-
-            tu_in_transaction = "salt.modules.transactional_update.in_transaction"
-            with patch(tu_in_transaction) as in_transaction_mock:
-                in_transaction_mock.return_value = in_transaction
-                with patch.dict(tu.__salt__, salt_mock):
-                    assert tu.pending_transaction() is result
-                    salt_mock["cmd.run_all"].assert_called_with(
-                        ["snapper", "--no-dbus", "list", "--columns", "number"]
-                    )
-
-    def test_pending_transaction_in_transaction(self):
-        """Test transactional_update.pending_transaction when in transaction"""
-        tu_in_transaction = "salt.modules.transactional_update.in_transaction"
-        with patch(tu_in_transaction) as in_transaction_mock:
-            in_transaction_mock.return_value = True
-            with pytest.raises(CommandExecutionError):
-                tu.pending_transaction()
-
-    def test_call_fails_input_validation(self):
-        """Test transactional_update.call missing function name"""
-        with pytest.raises(CommandExecutionError):
-            tu.call("")
-
-    @patch("tempfile.mkdtemp")
-    def test_call_fails_untar(self, mkdtemp):
-        """Test transactional_update.call when tar fails"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value="Error"),
-            "config.option": MagicMock(),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert tu.call("/chroot", "test.ping") == {
-                "result": False,
-                "comment": "Error",
-            }
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            utils_mock["files.rm_rf"].assert_called_once()
-
-    @patch("tempfile.mkdtemp")
-    def test_call_fails_salt_thin(self, mkdtemp):
-        """Test transactional_update.chroot when fails salt_thin"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-            "json.find_json": MagicMock(side_effect=ValueError()),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value=""),
-            "config.option": MagicMock(),
-            "cmd.run_all": MagicMock(return_value={"retcode": 1, "stderr": "Error"}),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert tu.call("test.ping") == {
-                "result": False,
-                "retcode": 1,
-                "comment": "Error",
-            }
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--continue",
-                    "--quiet",
-                    "run",
-                    "python{}".format(sys.version_info[0]),
-                    "/var/cache/salt/minion/tmp01/salt-call",
-                    "--metadata",
-                    "--local",
-                    "--log-file",
-                    "/var/cache/salt/minion/tmp01/log",
-                    "--cachedir",
-                    "/var/cache/salt/minion/tmp01/cache",
-                    "--out",
-                    "json",
-                    "-l",
-                    "quiet",
-                    "--",
-                    "test.ping",
-                ]
-            )
-            utils_mock["files.rm_rf"].assert_called_once()
-
-    @patch("tempfile.mkdtemp")
-    def test_call_fails_function(self, mkdtemp):
-        """Test transactional_update.chroot when fails the function"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-            "json.find_json": MagicMock(side_effect=ValueError()),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value=""),
-            "config.option": MagicMock(),
-            "cmd.run_all": MagicMock(
-                return_value={"retcode": 0, "stdout": "Not found", "stderr": ""}
-            ),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert tu.call("test.ping") == {
-                "result": False,
-                "retcode": 1,
-                "comment": "Not found",
-            }
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--continue",
-                    "--quiet",
-                    "run",
-                    "python{}".format(sys.version_info[0]),
-                    "/var/cache/salt/minion/tmp01/salt-call",
-                    "--metadata",
-                    "--local",
-                    "--log-file",
-                    "/var/cache/salt/minion/tmp01/log",
-                    "--cachedir",
-                    "/var/cache/salt/minion/tmp01/cache",
-                    "--out",
-                    "json",
-                    "-l",
-                    "quiet",
-                    "--",
-                    "test.ping",
-                ]
-            )
-            utils_mock["files.rm_rf"].assert_called_once()
-
-    @patch("tempfile.mkdtemp")
-    def test_call_success_no_reboot(self, mkdtemp):
-        """Test transactional_update.chroot when succeed"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-            "json.find_json": MagicMock(return_value={"return": "result"}),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value=""),
-            "config.option": MagicMock(),
-            "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert tu.call("test.ping") == "result"
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--continue",
-                    "--quiet",
-                    "run",
-                    "python{}".format(sys.version_info[0]),
-                    "/var/cache/salt/minion/tmp01/salt-call",
-                    "--metadata",
-                    "--local",
-                    "--log-file",
-                    "/var/cache/salt/minion/tmp01/log",
-                    "--cachedir",
-                    "/var/cache/salt/minion/tmp01/cache",
-                    "--out",
-                    "json",
-                    "-l",
-                    "quiet",
-                    "--",
-                    "test.ping",
-                ]
-            )
-            utils_mock["files.rm_rf"].assert_called_once()
-
-    @patch("salt.modules.transactional_update.reboot")
-    @patch("salt.modules.transactional_update.pending_transaction")
-    @patch("tempfile.mkdtemp")
-    def test_call_success_reboot(self, mkdtemp, pending_transaction, reboot):
-        """Test transactional_update.chroot when succeed and reboot"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        pending_transaction.return_value = True
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-            "json.find_json": MagicMock(return_value={"return": "result"}),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value=""),
-            "config.option": MagicMock(),
-            "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert (
-                tu.call("transactional_update.dup", activate_transaction=True)
-                == "result"
-            )
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--continue",
-                    "--quiet",
-                    "run",
-                    "python{}".format(sys.version_info[0]),
-                    "/var/cache/salt/minion/tmp01/salt-call",
-                    "--metadata",
-                    "--local",
-                    "--log-file",
-                    "/var/cache/salt/minion/tmp01/log",
-                    "--cachedir",
-                    "/var/cache/salt/minion/tmp01/cache",
-                    "--out",
-                    "json",
-                    "-l",
-                    "quiet",
-                    "--",
-                    "transactional_update.dup",
-                ]
-            )
-            utils_mock["files.rm_rf"].assert_called_once()
-            pending_transaction.assert_called_once()
-            reboot.assert_called_once()
-
-    @patch("tempfile.mkdtemp")
-    def test_call_success_parameters(self, mkdtemp):
-        """Test transactional_update.chroot when succeed with parameters"""
-        mkdtemp.return_value = "/var/cache/salt/minion/tmp01"
-        utils_mock = {
-            "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"),
-            "files.rm_rf": MagicMock(),
-            "json.find_json": MagicMock(return_value={"return": "result"}),
-        }
-        opts_mock = {"cachedir": "/var/cache/salt/minion"}
-        salt_mock = {
-            "cmd.run": MagicMock(return_value=""),
-            "config.option": MagicMock(),
-            "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}),
-        }
-        with patch.dict(tu.__utils__, utils_mock), patch.dict(
-            tu.__opts__, opts_mock
-        ), patch.dict(tu.__salt__, salt_mock):
-            assert tu.call("module.function", key="value") == "result"
-
-            utils_mock["thin.gen_thin"].assert_called_once()
-            salt_mock["config.option"].assert_called()
-            salt_mock["cmd.run"].assert_called_once()
-            salt_mock["cmd.run_all"].assert_called_with(
-                [
-                    "transactional-update",
-                    "--non-interactive",
-                    "--drop-if-no-change",
-                    "--no-selfupdate",
-                    "--continue",
-                    "--quiet",
-                    "run",
-                    "python{}".format(sys.version_info[0]),
-                    "/var/cache/salt/minion/tmp01/salt-call",
-                    "--metadata",
-                    "--local",
-                    "--log-file",
-                    "/var/cache/salt/minion/tmp01/log",
-                    "--cachedir",
-                    "/var/cache/salt/minion/tmp01/cache",
-                    "--out",
-                    "json",
-                    "-l",
-                    "quiet",
-                    "--",
-                    "module.function",
-                    "key=value",
-                ]
-            )
-            utils_mock["files.rm_rf"].assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_sls(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.sls"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-        TransactionalUpdateHighstate.render_highstate.return_value = (None, [])
-        TransactionalUpdateHighstate.state.reconcile_extend.return_value = (None, [])
-        TransactionalUpdateHighstate.state.requisite_in.return_value = (None, [])
-        TransactionalUpdateHighstate.state.verify_high.return_value = []
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(return_value=[]),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.sls("module") == "result"
-            _create_and_execute_salt_state.assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_sls_queue_true(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.sls"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-        TransactionalUpdateHighstate.render_highstate.return_value = (None, [])
-        TransactionalUpdateHighstate.state.reconcile_extend.return_value = (None, [])
-        TransactionalUpdateHighstate.state.requisite_in.return_value = (None, [])
-        TransactionalUpdateHighstate.state.verify_high.return_value = []
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.sls("module", queue=True) == "result"
-            _create_and_execute_salt_state.assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_sls_queue_false_failing(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.sls"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-        TransactionalUpdateHighstate.render_highstate.return_value = (None, [])
-        TransactionalUpdateHighstate.state.reconcile_extend.return_value = (None, [])
-        TransactionalUpdateHighstate.state.requisite_in.return_value = (None, [])
-        TransactionalUpdateHighstate.state.verify_high.return_value = []
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.sls("module", queue=False) == [
-                'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
-            ]
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_highstate(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.highstage"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(return_value=[]),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.highstate() == "result"
-            _create_and_execute_salt_state.assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_highstate_queue_true(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.highstage"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.highstate(queue=True) == "result"
-            _create_and_execute_salt_state.assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.modules.transactional_update.TransactionalUpdateHighstate")
-    @patch("salt.fileclient.get_file_client")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_highstate_queue_false_failing(
-        self,
-        get_sls_opts,
-        get_file_client,
-        TransactionalUpdateHighstate,
-        _create_and_execute_salt_state,
-    ):
-        """Test transactional_update.highstage"""
-        TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.highstate(queue=False) == [
-                'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
-            ]
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.client.ssh.state.SSHState")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_single(self, get_sls_opts, SSHState, _create_and_execute_salt_state):
-        """Test transactional_update.single"""
-        SSHState.return_value = SSHState
-        SSHState.verify_data.return_value = None
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(return_value=[]),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.single("pkg.installed", name="emacs") == "result"
-            _create_and_execute_salt_state.assert_called_once()
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.client.ssh.state.SSHState")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_single_queue_false_failing(
-        self, get_sls_opts, SSHState, _create_and_execute_salt_state
-    ):
-        """Test transactional_update.single"""
-        SSHState.return_value = SSHState
-        SSHState.verify_data.return_value = None
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.single("pkg.installed", name="emacs", queue=False) == [
-                'The function "state.running" is running as PID 4126 and was started at 2015, Mar 25 12:34:07.204096 with jid 20150325123407204096'
-            ]
-
-    @patch("salt.modules.transactional_update._create_and_execute_salt_state")
-    @patch("salt.client.ssh.state.SSHState")
-    @patch("salt.utils.state.get_sls_opts")
-    def test_single_queue_true(
-        self, get_sls_opts, SSHState, _create_and_execute_salt_state
-    ):
-        """Test transactional_update.single"""
-        SSHState.return_value = SSHState
-        SSHState.verify_data.return_value = None
-
-        _create_and_execute_salt_state.return_value = "result"
-        opts_mock = {
-            "hash_type": "md5",
-        }
-        salt_mock = {
-            "saltutil.is_running": MagicMock(
-                side_effect=[
-                    [
-                        {
-                            "fun": "state.running",
-                            "pid": "4126",
-                            "jid": "20150325123407204096",
-                        }
-                    ],
-                    [],
-                ]
-            ),
-        }
-        get_sls_opts.return_value = opts_mock
-        with patch.dict(tu.__opts__, opts_mock), patch.dict(
-            statemod.__salt__, salt_mock
-        ):
-            assert tu.single("pkg.installed", name="emacs", queue=True) == "result"
-            _create_and_execute_salt_state.assert_called_once()
-- 
2.33.1


