From 1b1bbc3e46ab2eed98f07a23368877fc068dbc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Mon, 26 Feb 2024 11:25:22 +0000 Subject: [PATCH] Fix problematic tests and allow smooth tests executions on containers * Align boto imports in tests with Salt modules Some modules `import boto` to set log levels. The related tests don't import `boto`. This can cause a problem when `boto` is not available. Tests are skipped when HAS_BOTO in the test_boto_*.py is False. Not trying to `import boto` can leave HAS_BOTO=True in the test file even though HAS_BOTO is False on the application side. In this case, tests are not skipped and fail. * Fix mock order in test_dig (test_network.py) `salt.utils.path.which` should not be mocked before `network.__utils__`. The Salt loader calls `salt.utils.network.linux_interfaces`, which needs the real `salt.utils.path.which`. * Fix mock calls Signed-off-by: Pedro Algarvio (cherry picked from commit 3506e7fd0e84320b2873370f1fe527025c244dca) * Skip venafiapi test if vcert module not available The same HAS_VCERT check is done in the runner module. * Moving tests/integration/modules/test_cmdmod.py to pytest, Gareth J Greenaway original author (cherry picked from commit 2c1040b4c2885efaa86576fd88eb36bb550b5996) * The repo.saltproject.io `index.html` file changed it's contents. Fix tests. Signed-off-by: Pedro Algarvio (cherry picked from commit 672f6586d7c3cdb0e8c5ee42524895035aafcc23) * Skip hwclock test when executed inside a container * Skip git pillar tests when executed inside a container These tests require a Git repository container, which is hard to correctly set up when executing the tests in the container in GH Actions. Using --network host can help, but there was still an error (the git repos were empty) when I tried to set this up. * Skip test requiring systemd inside container * Exclude tests for hgfs if missing hglib * Skip and fix tests when running on containers * Fix some failing test causing problem in SUSE environments * Skip more tests when running on containers * Use skipif instead of skip_if as it seems not behaving equally * Skip more tests that cannot run in a container * Remove SSH test which doesn't make sense after lock mechanism * Fix failing boto tests * Skip unmaintained tests upstream around zcbuildout * Skip some tests that does not run well in GH Actions --------- Co-authored-by: Pedro Algarvio Co-authored-by: Gareth J. Greenaway Co-authored-by: Alexander Graul --- .../integration/externalapi/test_venafiapi.py | 10 +- tests/integration/modules/test_cmdmod.py | 634 ------------------ tests/integration/modules/test_cp.py | 24 +- tests/integration/modules/test_timezone.py | 3 + tests/integration/pillar/test_git_pillar.py | 3 + tests/integration/ssh/test_state.py | 47 -- tests/pytests/functional/cache/test_consul.py | 4 + tests/pytests/functional/cache/test_mysql.py | 4 + .../functional/fileserver/hgfs/test_hgfs.py | 2 + .../pytests/functional/modules/test_cmdmod.py | 561 ++++++++++++++++ .../functional/modules/test_dockermod.py | 4 + .../pytests/functional/modules/test_swarm.py | 5 + .../pytests/functional/modules/test_system.py | 3 + .../pillar/hg_pillar/test_hg_pillar.py | 1 + .../states/rabbitmq/test_cluster.py | 4 + .../functional/states/rabbitmq/test_plugin.py | 4 + .../functional/states/rabbitmq/test_policy.py | 4 + .../states/rabbitmq/test_upstream.py | 4 + .../functional/states/rabbitmq/test_user.py | 4 + .../functional/states/rabbitmq/test_vhost.py | 4 + .../functional/states/test_docker_network.py | 5 + tests/pytests/functional/states/test_pkg.py | 6 +- .../integration/cli/test_syndic_eauth.py | 3 + .../integration/daemons/test_memory_leak.py | 4 + .../integration/modules/test_cmdmod.py | 93 +++ .../pytests/integration/modules/test_virt.py | 4 + tests/pytests/integration/ssh/test_log.py | 3 + tests/pytests/integration/ssh/test_master.py | 5 + .../integration/ssh/test_py_versions.py | 3 + .../pytests/integration/ssh/test_ssh_setup.py | 2 + .../scenarios/compat/test_with_versions.py | 4 + .../multimaster/test_failover_master.py | 3 + tests/pytests/scenarios/setup/test_install.py | 6 + tests/pytests/unit/modules/test_aptpkg.py | 12 +- .../pytests/unit/modules/test_linux_sysctl.py | 8 +- tests/pytests/unit/modules/test_win_ip.py | 4 +- tests/pytests/unit/test_master.py | 2 +- tests/pytests/unit/test_minion.py | 4 +- tests/pytests/unit/utils/event/test_event.py | 24 +- tests/unit/modules/test_boto_apigateway.py | 1 + .../unit/modules/test_boto_cognitoidentity.py | 1 + .../modules/test_boto_elasticsearch_domain.py | 1 + tests/unit/modules/test_boto_lambda.py | 1 + tests/unit/modules/test_network.py | 6 +- tests/unit/modules/test_nilrt_ip.py | 4 +- tests/unit/modules/test_zcbuildout.py | 2 + .../unit/netapi/rest_tornado/test_saltnado.py | 22 +- tests/unit/states/test_boto_apigateway.py | 1 + .../unit/states/test_boto_cognitoidentity.py | 1 + tests/unit/states/test_zcbuildout.py | 1 + 50 files changed, 824 insertions(+), 741 deletions(-) delete mode 100644 tests/integration/modules/test_cmdmod.py create mode 100644 tests/pytests/functional/modules/test_cmdmod.py diff --git a/tests/integration/externalapi/test_venafiapi.py b/tests/integration/externalapi/test_venafiapi.py index ad08605430f..3ae1e3392d8 100644 --- a/tests/integration/externalapi/test_venafiapi.py +++ b/tests/integration/externalapi/test_venafiapi.py @@ -13,6 +13,14 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.x509.oid import NameOID +try: + import vcert + from vcert.common import CertificateRequest + + HAS_VCERT = True +except ImportError: + HAS_VCERT = False + from tests.support.case import ShellCase @@ -36,6 +44,7 @@ def with_random_name(func): return wrapper +@pytest.mark.skipif(HAS_VCERT is False, reason="The vcert module must be installed.") class VenafiTest(ShellCase): """ Test the venafi runner @@ -86,7 +95,6 @@ class VenafiTest(ShellCase): @with_random_name @pytest.mark.slow_test def test_sign(self, name): - csr_pem = """-----BEGIN CERTIFICATE REQUEST----- MIIFbDCCA1QCAQAwgbQxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIDARVdGFoMRIwEAYD VQQHDAlTYWx0IExha2UxFDASBgNVBAoMC1ZlbmFmaSBJbmMuMRQwEgYDVQQLDAtJ diff --git a/tests/integration/modules/test_cmdmod.py b/tests/integration/modules/test_cmdmod.py deleted file mode 100644 index 800111174f0..00000000000 --- a/tests/integration/modules/test_cmdmod.py +++ /dev/null @@ -1,634 +0,0 @@ -import os -import random -import sys -import tempfile -from contextlib import contextmanager - -import pytest - -import salt.utils.path -import salt.utils.platform -import salt.utils.user -from tests.support.case import ModuleCase -from tests.support.helpers import SKIP_INITIAL_PHOTONOS_FAILURES, dedent -from tests.support.runtests import RUNTIME_VARS - -AVAILABLE_PYTHON_EXECUTABLE = salt.utils.path.which_bin( - ["python", "python2", "python2.6", "python2.7"] -) - - -@pytest.mark.windows_whitelisted -class CMDModuleTest(ModuleCase): - """ - Validate the cmd module - """ - - def setUp(self): - self.runas_usr = "nobody" - if salt.utils.platform.is_darwin(): - self.runas_usr = "macsalttest" - - @contextmanager - def _ensure_user_exists(self, name): - if name in self.run_function("user.info", [name]).values(): - # User already exists; don't touch - yield - else: - # Need to create user for test - self.run_function("user.add", [name]) - try: - yield - finally: - self.run_function("user.delete", [name], remove=True) - - @pytest.mark.slow_test - @pytest.mark.skip_on_windows - def test_run(self): - """ - cmd.run - """ - shell = os.environ.get("SHELL") - if shell is None: - # Failed to get the SHELL var, don't run - self.skipTest("Unable to get the SHELL environment variable") - - self.assertTrue(self.run_function("cmd.run", ["echo $SHELL"])) - self.assertEqual( - self.run_function( - "cmd.run", ["echo $SHELL", "shell={}".format(shell)], python_shell=True - ).rstrip(), - shell, - ) - self.assertEqual( - self.run_function("cmd.run", ["ls / | grep etc"], python_shell=True), "etc" - ) - self.assertEqual( - self.run_function( - "cmd.run", - ['echo {{grains.id}} | awk "{print $1}"'], - template="jinja", - python_shell=True, - ), - "minion", - ) - self.assertEqual( - self.run_function( - "cmd.run", ["grep f"], stdin="one\ntwo\nthree\nfour\nfive\n" - ), - "four\nfive", - ) - self.assertEqual( - self.run_function( - "cmd.run", ['echo "a=b" | sed -e s/=/:/g'], python_shell=True - ), - "a:b", - ) - - @pytest.mark.slow_test - def test_stdout(self): - """ - cmd.run_stdout - """ - self.assertEqual( - self.run_function("cmd.run_stdout", ['echo "cheese"']).rstrip(), - "cheese" if not salt.utils.platform.is_windows() else '"cheese"', - ) - - @pytest.mark.slow_test - def test_stderr(self): - """ - cmd.run_stderr - """ - if sys.platform.startswith(("freebsd", "openbsd")): - shell = "/bin/sh" - else: - shell = "/bin/bash" - - self.assertEqual( - self.run_function( - "cmd.run_stderr", - ['echo "cheese" 1>&2', "shell={}".format(shell)], - python_shell=True, - ).rstrip(), - "cheese" if not salt.utils.platform.is_windows() else '"cheese"', - ) - - @pytest.mark.slow_test - def test_run_all(self): - """ - cmd.run_all - """ - if sys.platform.startswith(("freebsd", "openbsd")): - shell = "/bin/sh" - else: - shell = "/bin/bash" - - ret = self.run_function( - "cmd.run_all", - ['echo "cheese" 1>&2', "shell={}".format(shell)], - python_shell=True, - ) - self.assertTrue("pid" in ret) - self.assertTrue("retcode" in ret) - self.assertTrue("stdout" in ret) - self.assertTrue("stderr" in ret) - self.assertTrue(isinstance(ret.get("pid"), int)) - self.assertTrue(isinstance(ret.get("retcode"), int)) - self.assertTrue(isinstance(ret.get("stdout"), str)) - self.assertTrue(isinstance(ret.get("stderr"), str)) - self.assertEqual( - ret.get("stderr").rstrip(), - "cheese" if not salt.utils.platform.is_windows() else '"cheese"', - ) - - @pytest.mark.slow_test - def test_retcode(self): - """ - cmd.retcode - """ - self.assertEqual( - self.run_function("cmd.retcode", ["exit 0"], python_shell=True), 0 - ) - self.assertEqual( - self.run_function("cmd.retcode", ["exit 1"], python_shell=True), 1 - ) - - @pytest.mark.slow_test - def test_run_all_with_success_retcodes(self): - """ - cmd.run with success_retcodes - """ - ret = self.run_function( - "cmd.run_all", ["exit 42"], success_retcodes=[42], python_shell=True - ) - - self.assertTrue("retcode" in ret) - self.assertEqual(ret.get("retcode"), 0) - - @pytest.mark.slow_test - def test_retcode_with_success_retcodes(self): - """ - cmd.run with success_retcodes - """ - ret = self.run_function( - "cmd.retcode", ["exit 42"], success_retcodes=[42], python_shell=True - ) - - self.assertEqual(ret, 0) - - @pytest.mark.slow_test - def test_run_all_with_success_stderr(self): - """ - cmd.run with success_retcodes - """ - random_file = "{}{}{}".format( - RUNTIME_VARS.TMP_ROOT_DIR, os.path.sep, random.random() - ) - - if salt.utils.platform.is_windows(): - func = "type" - expected_stderr = "cannot find the file specified" - else: - func = "cat" - expected_stderr = "No such file or directory" - ret = self.run_function( - "cmd.run_all", - ["{} {}".format(func, random_file)], - success_stderr=[expected_stderr], - python_shell=True, - ) - - self.assertTrue("retcode" in ret) - self.assertEqual(ret.get("retcode"), 0) - - @pytest.mark.slow_test - def test_blacklist_glob(self): - """ - cmd_blacklist_glob - """ - self.assertEqual( - self.run_function("cmd.run", ["bad_command --foo"]).rstrip(), - 'ERROR: The shell command "bad_command --foo" is not permitted', - ) - - @pytest.mark.slow_test - def test_script(self): - """ - cmd.script - """ - args = "saltines crackers biscuits=yes" - script = "salt://script.py" - ret = self.run_function("cmd.script", [script, args], saltenv="base") - self.assertEqual(ret["stdout"], args) - - @pytest.mark.slow_test - def test_script_query_string(self): - """ - cmd.script - """ - args = "saltines crackers biscuits=yes" - script = "salt://script.py?saltenv=base" - ret = self.run_function("cmd.script", [script, args], saltenv="base") - self.assertEqual(ret["stdout"], args) - - @pytest.mark.slow_test - def test_script_retcode(self): - """ - cmd.script_retcode - """ - script = "salt://script.py" - ret = self.run_function("cmd.script_retcode", [script], saltenv="base") - self.assertEqual(ret, 0) - - @pytest.mark.slow_test - def test_script_cwd(self): - """ - cmd.script with cwd - """ - tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - args = "saltines crackers biscuits=yes" - script = "salt://script.py" - ret = self.run_function( - "cmd.script", [script, args], cwd=tmp_cwd, saltenv="base" - ) - self.assertEqual(ret["stdout"], args) - - @pytest.mark.slow_test - def test_script_cwd_with_space(self): - """ - cmd.script with cwd - """ - tmp_cwd = "{}{}test 2".format( - tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), os.path.sep - ) - os.mkdir(tmp_cwd) - - args = "saltines crackers biscuits=yes" - script = "salt://script.py" - ret = self.run_function( - "cmd.script", [script, args], cwd=tmp_cwd, saltenv="base" - ) - self.assertEqual(ret["stdout"], args) - - @pytest.mark.destructive_test - def test_tty(self): - """ - cmd.tty - """ - for tty in ("tty0", "pts3"): - if os.path.exists(os.path.join("/dev", tty)): - ret = self.run_function("cmd.tty", [tty, "apply salt liberally"]) - self.assertTrue("Success" in ret) - - @pytest.mark.skip_on_windows - @pytest.mark.skip_if_binaries_missing("which") - def test_which(self): - """ - cmd.which - """ - cmd_which = self.run_function("cmd.which", ["cat"]) - self.assertIsInstance(cmd_which, str) - cmd_run = self.run_function("cmd.run", ["which cat"]) - self.assertIsInstance(cmd_run, str) - self.assertEqual(cmd_which.rstrip(), cmd_run.rstrip()) - - @pytest.mark.skip_on_windows - @pytest.mark.skip_if_binaries_missing("which") - def test_which_bin(self): - """ - cmd.which_bin - """ - cmds = ["pip3", "pip2", "pip", "pip-python"] - ret = self.run_function("cmd.which_bin", [cmds]) - self.assertTrue(os.path.split(ret)[1] in cmds) - - @pytest.mark.slow_test - def test_has_exec(self): - """ - cmd.has_exec - """ - self.assertTrue( - self.run_function("cmd.has_exec", [AVAILABLE_PYTHON_EXECUTABLE]) - ) - self.assertFalse( - self.run_function("cmd.has_exec", ["alllfsdfnwieulrrh9123857ygf"]) - ) - - @pytest.mark.slow_test - def test_exec_code(self): - """ - cmd.exec_code - """ - code = dedent( - """ - import sys - sys.stdout.write('cheese') - """ - ) - self.assertEqual( - self.run_function( - "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code] - ).rstrip(), - "cheese", - ) - - @pytest.mark.slow_test - def test_exec_code_with_single_arg(self): - """ - cmd.exec_code - """ - code = dedent( - """ - import sys - sys.stdout.write(sys.argv[1]) - """ - ) - arg = "cheese" - self.assertEqual( - self.run_function( - "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code], args=arg - ).rstrip(), - arg, - ) - - @pytest.mark.slow_test - def test_exec_code_with_multiple_args(self): - """ - cmd.exec_code - """ - code = dedent( - """ - import sys - sys.stdout.write(sys.argv[1]) - """ - ) - arg = "cheese" - self.assertEqual( - self.run_function( - "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code], args=[arg, "test"] - ).rstrip(), - arg, - ) - - @pytest.mark.slow_test - def test_quotes(self): - """ - cmd.run with quoted command - """ - cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ - expected_result = 'SELECT * FROM foo WHERE bar="baz"' - if salt.utils.platform.is_windows(): - expected_result = "'SELECT * FROM foo WHERE bar=\"baz\"'" - result = self.run_function("cmd.run_stdout", [cmd]).strip() - self.assertEqual(result, expected_result) - - @pytest.mark.skip_if_not_root - @pytest.mark.skip_on_windows(reason="Skip on Windows, requires password") - def test_quotes_runas(self): - """ - cmd.run with quoted command - """ - cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ - expected_result = 'SELECT * FROM foo WHERE bar="baz"' - result = self.run_function( - "cmd.run_all", [cmd], runas=RUNTIME_VARS.RUNNING_TESTS_USER - ) - errmsg = "The command returned: {}".format(result) - self.assertEqual(result["retcode"], 0, errmsg) - self.assertEqual(result["stdout"], expected_result, errmsg) - - @pytest.mark.destructive_test - @pytest.mark.skip_if_not_root - @pytest.mark.skip_on_windows(reason="Skip on Windows, uses unix commands") - @pytest.mark.slow_test - def test_avoid_injecting_shell_code_as_root(self): - """ - cmd.run should execute the whole command as the "runas" user, not - running substitutions as root. - """ - cmd = "echo $(id -u)" - - root_id = self.run_function("cmd.run_stdout", [cmd]) - runas_root_id = self.run_function( - "cmd.run_stdout", [cmd], runas=RUNTIME_VARS.RUNNING_TESTS_USER - ) - with self._ensure_user_exists(self.runas_usr): - user_id = self.run_function("cmd.run_stdout", [cmd], runas=self.runas_usr) - - self.assertNotEqual(user_id, root_id) - self.assertNotEqual(user_id, runas_root_id) - self.assertEqual(root_id, runas_root_id) - - @pytest.mark.destructive_test - @pytest.mark.skip_if_not_root - @pytest.mark.skip_on_windows(reason="Skip on Windows, uses unix commands") - @pytest.mark.slow_test - def test_cwd_runas(self): - """ - cmd.run should be able to change working directory correctly, whether - or not runas is in use. - """ - cmd = "pwd" - tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - os.chmod(tmp_cwd, 0o711) - - cwd_normal = self.run_function("cmd.run_stdout", [cmd], cwd=tmp_cwd).rstrip( - "\n" - ) - self.assertEqual(tmp_cwd, cwd_normal) - - with self._ensure_user_exists(self.runas_usr): - cwd_runas = self.run_function( - "cmd.run_stdout", [cmd], cwd=tmp_cwd, runas=self.runas_usr - ).rstrip("\n") - self.assertEqual(tmp_cwd, cwd_runas) - - @pytest.mark.destructive_test - @pytest.mark.skip_if_not_root - @pytest.mark.skip_unless_on_darwin(reason="Applicable to MacOS only") - @pytest.mark.slow_test - def test_runas_env(self): - """ - cmd.run should be able to change working directory correctly, whether - or not runas is in use. - """ - with self._ensure_user_exists(self.runas_usr): - user_path = self.run_function( - "cmd.run_stdout", ['printf %s "$PATH"'], runas=self.runas_usr - ) - # XXX: Not sure of a better way. Environment starts out with - # /bin:/usr/bin and should be populated by path helper and the bash - # profile. - self.assertNotEqual("/bin:/usr/bin", user_path) - - @pytest.mark.destructive_test - @pytest.mark.skip_if_not_root - @pytest.mark.skip_unless_on_darwin(reason="Applicable to MacOS only") - @pytest.mark.slow_test - def test_runas_complex_command_bad_cwd(self): - """ - cmd.run should not accidentally run parts of a complex command when - given a cwd which cannot be used by the user the command is run as. - - Due to the need to use `su -l` to login to another user on MacOS, we - cannot cd into directories that the target user themselves does not - have execute permission for. To an extent, this test is testing that - buggy behaviour, but its purpose is to ensure that the greater bug of - running commands after failing to cd does not occur. - """ - tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - os.chmod(tmp_cwd, 0o700) - - with self._ensure_user_exists(self.runas_usr): - cmd_result = self.run_function( - "cmd.run_all", - ['pwd; pwd; : $(echo "You have failed the test" >&2)'], - cwd=tmp_cwd, - runas=self.runas_usr, - ) - - self.assertEqual("", cmd_result["stdout"]) - self.assertNotIn("You have failed the test", cmd_result["stderr"]) - self.assertNotEqual(0, cmd_result["retcode"]) - - @SKIP_INITIAL_PHOTONOS_FAILURES - @pytest.mark.skip_on_windows - @pytest.mark.skip_if_not_root - @pytest.mark.destructive_test - @pytest.mark.slow_test - def test_runas(self): - """ - Ensure that the env is the runas user's - """ - with self._ensure_user_exists(self.runas_usr): - out = self.run_function( - "cmd.run", ["env"], runas=self.runas_usr - ).splitlines() - self.assertIn("USER={}".format(self.runas_usr), out) - - @pytest.mark.skip_if_binaries_missing("sleep", reason="sleep cmd not installed") - def test_timeout(self): - """ - cmd.run trigger timeout - """ - out = self.run_function( - "cmd.run", ["sleep 2 && echo hello"], f_timeout=1, python_shell=True - ) - self.assertTrue("Timed out" in out) - - @pytest.mark.skip_if_binaries_missing("sleep", reason="sleep cmd not installed") - def test_timeout_success(self): - """ - cmd.run sufficient timeout to succeed - """ - out = self.run_function( - "cmd.run", ["sleep 1 && echo hello"], f_timeout=2, python_shell=True - ) - self.assertEqual(out, "hello") - - @pytest.mark.slow_test - def test_hide_output(self): - """ - Test the hide_output argument - """ - ls_command = ( - ["ls", "/"] if not salt.utils.platform.is_windows() else ["dir", "c:\\"] - ) - - error_command = ["thiscommanddoesnotexist"] - - # cmd.run - out = self.run_function("cmd.run", ls_command, hide_output=True) - self.assertEqual(out, "") - - # cmd.shell - out = self.run_function("cmd.shell", ls_command, hide_output=True) - self.assertEqual(out, "") - - # cmd.run_stdout - out = self.run_function("cmd.run_stdout", ls_command, hide_output=True) - self.assertEqual(out, "") - - # cmd.run_stderr - out = self.run_function("cmd.shell", error_command, hide_output=True) - self.assertEqual(out, "") - - # cmd.run_all (command should have produced stdout) - out = self.run_function("cmd.run_all", ls_command, hide_output=True) - self.assertEqual(out["stdout"], "") - self.assertEqual(out["stderr"], "") - - # cmd.run_all (command should have produced stderr) - out = self.run_function("cmd.run_all", error_command, hide_output=True) - self.assertEqual(out["stdout"], "") - self.assertEqual(out["stderr"], "") - - @pytest.mark.slow_test - def test_cmd_run_whoami(self): - """ - test return of whoami - """ - if not salt.utils.platform.is_windows(): - user = RUNTIME_VARS.RUNTIME_CONFIGS["master"]["user"] - else: - user = salt.utils.user.get_specific_user() - if user.startswith("sudo_"): - user = user.replace("sudo_", "") - cmd = self.run_function("cmd.run", ["whoami"]) - try: - self.assertEqual(user.lower(), cmd.lower()) - except AssertionError as exc: - if not salt.utils.platform.is_windows(): - raise exc from None - if "\\" in user: - user = user.split("\\")[-1] - self.assertEqual(user.lower(), cmd.lower()) - - @pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") - @pytest.mark.slow_test - def test_windows_env_handling(self): - """ - Ensure that nt.environ is used properly with cmd.run* - """ - out = self.run_function( - "cmd.run", ["set"], env={"abc": "123", "ABC": "456"} - ).splitlines() - self.assertIn("abc=123", out) - self.assertIn("ABC=456", out) - - @pytest.mark.slow_test - @pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") - def test_windows_powershell_script_args(self): - """ - Ensure that powershell processes inline script in args - """ - val = "i like cheese" - args = ( - '-SecureString (ConvertTo-SecureString -String "{}" -AsPlainText -Force)' - " -ErrorAction Stop".format(val) - ) - script = "salt://issue-56195/test.ps1" - ret = self.run_function( - "cmd.script", [script], args=args, shell="powershell", saltenv="base" - ) - self.assertEqual(ret["stdout"], val) - - @pytest.mark.slow_test - @pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") - @pytest.mark.skip_if_binaries_missing("pwsh") - def test_windows_powershell_script_args_pwsh(self): - """ - Ensure that powershell processes inline script in args with powershell - core - """ - val = "i like cheese" - args = ( - '-SecureString (ConvertTo-SecureString -String "{}" -AsPlainText -Force)' - " -ErrorAction Stop".format(val) - ) - script = "salt://issue-56195/test.ps1" - ret = self.run_function( - "cmd.script", [script], args=args, shell="pwsh", saltenv="base" - ) - self.assertEqual(ret["stdout"], val) diff --git a/tests/integration/modules/test_cp.py b/tests/integration/modules/test_cp.py index ad7538b4ba8..cd3e4c2f5ad 100644 --- a/tests/integration/modules/test_cp.py +++ b/tests/integration/modules/test_cp.py @@ -234,9 +234,9 @@ class CPModuleTest(ModuleCase): self.run_function("cp.get_url", ["https://repo.saltproject.io/index.html", tgt]) with salt.utils.files.fopen(tgt, "r") as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) - self.assertIn("Bootstrap", data) - self.assertIn("Debian", data) - self.assertIn("Windows", data) + self.assertIn("Salt Project", data) + self.assertIn("Package", data) + self.assertIn("Repo", data) self.assertNotIn("AYBABTU", data) @pytest.mark.slow_test @@ -250,9 +250,9 @@ class CPModuleTest(ModuleCase): with salt.utils.files.fopen(ret, "r") as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) - self.assertIn("Bootstrap", data) - self.assertIn("Debian", data) - self.assertIn("Windows", data) + self.assertIn("Salt Project", data) + self.assertIn("Package", data) + self.assertIn("Repo", data) self.assertNotIn("AYBABTU", data) @pytest.mark.slow_test @@ -273,9 +273,9 @@ class CPModuleTest(ModuleCase): time.sleep(sleep) if ret.find("HTTP 599") != -1: raise Exception("https://repo.saltproject.io/index.html returned 599 error") - self.assertIn("Bootstrap", ret) - self.assertIn("Debian", ret) - self.assertIn("Windows", ret) + self.assertIn("Salt Project", ret) + self.assertIn("Package", ret) + self.assertIn("Repo", ret) self.assertNotIn("AYBABTU", ret) @pytest.mark.slow_test @@ -346,9 +346,9 @@ class CPModuleTest(ModuleCase): """ src = "https://repo.saltproject.io/index.html" ret = self.run_function("cp.get_file_str", [src]) - self.assertIn("Bootstrap", ret) - self.assertIn("Debian", ret) - self.assertIn("Windows", ret) + self.assertIn("Salt Project", ret) + self.assertIn("Package", ret) + self.assertIn("Repo", ret) self.assertNotIn("AYBABTU", ret) @pytest.mark.slow_test diff --git a/tests/integration/modules/test_timezone.py b/tests/integration/modules/test_timezone.py index 8d7180cbd13..c1dc8a7b73d 100644 --- a/tests/integration/modules/test_timezone.py +++ b/tests/integration/modules/test_timezone.py @@ -4,6 +4,7 @@ Integration tests for timezone module Linux and Solaris are supported """ import pytest +import os from tests.support.case import ModuleCase @@ -15,6 +16,8 @@ except ImportError: HAS_TZLOCAL = False +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" +@pytest.mark.skipif(INSIDE_CONTAINER, reason="No hwclock in a container") class TimezoneLinuxModuleTest(ModuleCase): def setUp(self): """ diff --git a/tests/integration/pillar/test_git_pillar.py b/tests/integration/pillar/test_git_pillar.py index 68c14daaa15..5b4cbda95c9 100644 --- a/tests/integration/pillar/test_git_pillar.py +++ b/tests/integration/pillar/test_git_pillar.py @@ -63,6 +63,7 @@ https://github.com/git/git/commit/6bc0cb5 https://github.com/unbit/uwsgi/commit/ac1e354 """ +import os import random import string import sys @@ -100,9 +101,11 @@ try: except Exception: # pylint: disable=broad-except HAS_PYGIT2 = False +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" pytestmark = [ SKIP_INITIAL_PHOTONOS_FAILURES, pytest.mark.skip_on_platforms(windows=True, darwin=True), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Communication problems between containers."), ] diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index a9fd3e7f2d3..69245454e85 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -283,53 +283,6 @@ class SSHStateTest(SSHCase): check_file = self.run_function("file.file_exists", [SSH_SLS_FILE], wipe=False) self.assertTrue(check_file) - @pytest.mark.slow_test - def test_state_running(self): - """ - test state.running with salt-ssh - """ - - retval = [] - - def _run_in_background(): - retval.append(self.run_function("state.sls", ["running"], wipe=False)) - - bg_thread = threading.Thread(target=_run_in_background) - bg_thread.start() - - expected = 'The function "state.pkg" is running as' - state_ret = [] - for _ in range(30): - if not bg_thread.is_alive(): - continue - get_sls = self.run_function("state.running", wipe=False) - state_ret.append(get_sls) - if expected in " ".join(get_sls): - # We found the expected return - break - time.sleep(1) - else: - if not bg_thread.is_alive(): - bg_failed_msg = "Failed to return clean data" - if retval and bg_failed_msg in retval.pop().get("_error", ""): - pytest.skip("Background state run failed, skipping") - self.fail( - "Did not find '{}' in state.running return: {}".format( - expected, state_ret - ) - ) - - # make sure we wait until the earlier state is complete - future = time.time() + 120 - while True: - if expected not in " ".join(self.run_function("state.running", wipe=False)): - break - if time.time() > future: - self.fail( - "state.pkg is still running overtime. Test did not clean up" - " correctly." - ) - def tearDown(self): """ make sure to clean up any old ssh directories diff --git a/tests/pytests/functional/cache/test_consul.py b/tests/pytests/functional/cache/test_consul.py index 3a38e495a93..c6e16d2588e 100644 --- a/tests/pytests/functional/cache/test_consul.py +++ b/tests/pytests/functional/cache/test_consul.py @@ -1,4 +1,5 @@ import logging +import os import socket import time @@ -13,9 +14,12 @@ docker = pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/cache/test_mysql.py b/tests/pytests/functional/cache/test_mysql.py index c283872c08c..e15fc732a4a 100644 --- a/tests/pytests/functional/cache/test_mysql.py +++ b/tests/pytests/functional/cache/test_mysql.py @@ -1,4 +1,5 @@ import logging +import os import pytest @@ -11,9 +12,12 @@ docker = pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/fileserver/hgfs/test_hgfs.py b/tests/pytests/functional/fileserver/hgfs/test_hgfs.py index 571fe75e403..bfd927fd0fe 100644 --- a/tests/pytests/functional/fileserver/hgfs/test_hgfs.py +++ b/tests/pytests/functional/fileserver/hgfs/test_hgfs.py @@ -16,6 +16,8 @@ try: except ImportError: HAS_HG = False +pytestmark = [pytest.mark.skipif(not HAS_HG, reason="missing hglib library")] + @pytest.fixture(scope="module") def configure_loader_modules(master_opts): diff --git a/tests/pytests/functional/modules/test_cmdmod.py b/tests/pytests/functional/modules/test_cmdmod.py new file mode 100644 index 00000000000..d30b474c6d2 --- /dev/null +++ b/tests/pytests/functional/modules/test_cmdmod.py @@ -0,0 +1,561 @@ +import os +import random +import sys +from contextlib import contextmanager + +import pytest + +import salt.config +import salt.utils.path +import salt.utils.platform +import salt.utils.user +from tests.support.helpers import SKIP_INITIAL_PHOTONOS_FAILURES, dedent + +pytestmark = [pytest.mark.windows_whitelisted] + + +@pytest.fixture(scope="module") +def cmdmod(modules): + return modules.cmd + + +@pytest.fixture(scope="module") +def usermod(modules): + return modules.user + + +@pytest.fixture(scope="module") +def available_python_executable(): + yield salt.utils.path.which_bin(["python", "python3"]) + + +@pytest.fixture +def runas_usr(): + runas_usr = "nobody" + if salt.utils.platform.is_darwin(): + runas_usr = "macsalttest" + yield runas_usr + + +@pytest.fixture +def running_username(): + """ + Return the username that is running the code. + """ + return salt.utils.user.get_user() + + +@pytest.fixture +def script_contents(state_tree): + _contents = """ + #!/usr/bin/env python3 + import sys + print(" ".join(sys.argv[1:])) + """ + + with pytest.helpers.temp_file("script.py", _contents, state_tree): + yield + + +@pytest.fixture +def issue_56195_test_ps1(state_tree): + _contents = """ + [CmdLetBinding()] + Param( + [SecureString] $SecureString + ) + $Credential = New-Object System.Net.NetworkCredential("DummyId", $SecureString) + $Credential.Password + """ + + with pytest.helpers.temp_file("issue_56195_test.ps1", _contents, state_tree): + yield + + +@contextmanager +def _ensure_user_exists(name, usermod): + if name in usermod.info(name).values(): + # User already exists; don't touch + yield + else: + # Need to create user for test + usermod.add(name) + try: + yield + finally: + usermod.delete(name, remove=True) + + +@pytest.mark.slow_test +def test_run(cmdmod): + """ + cmd.run + """ + shell = os.environ.get("SHELL") + if shell is None: + # Failed to get the SHELL var, don't run + pytest.skip("Unable to get the SHELL environment variable") + + assert cmdmod.run("echo $SHELL") + assert cmdmod.run("echo $SHELL", shell=shell, python_shell=True).rstrip() == shell + assert cmdmod.run("ls / | grep etc", python_shell=True) == "etc" + assert ( + cmdmod.run( + 'echo {{grains.id}} | awk "{print $1}"', + template="jinja", + python_shell=True, + ) + == "func-tests-minion" + ) + assert cmdmod.run("grep f", stdin="one\ntwo\nthree\nfour\nfive\n") == "four\nfive" + assert cmdmod.run('echo "a=b" | sed -e s/=/:/g', python_shell=True) == "a:b" + + +@pytest.mark.slow_test +def test_stdout(cmdmod): + """ + cmd.run_stdout + """ + assert ( + cmdmod.run_stdout('echo "cheese"').rstrip() == "cheese" + if not salt.utils.platform.is_windows() + else '"cheese"' + ) + + +@pytest.mark.slow_test +def test_stderr(cmdmod): + """ + cmd.run_stderr + """ + if sys.platform.startswith(("freebsd", "openbsd")): + shell = "/bin/sh" + else: + shell = "/bin/bash" + + assert ( + cmdmod.run_stderr( + 'echo "cheese" 1>&2', + shell=shell, + python_shell=True, + ).rstrip() + == "cheese" + if not salt.utils.platform.is_windows() + else '"cheese"' + ) + + +@pytest.mark.slow_test +def test_run_all(cmdmod): + """ + cmd.run_all + """ + if sys.platform.startswith(("freebsd", "openbsd")): + shell = "/bin/sh" + else: + shell = "/bin/bash" + + ret = cmdmod.run_all( + 'echo "cheese" 1>&2', + shell=shell, + python_shell=True, + ) + assert "pid" in ret + assert "retcode" in ret + assert "stdout" in ret + assert "stderr" in ret + assert isinstance(ret.get("pid"), int) + assert isinstance(ret.get("retcode"), int) + assert isinstance(ret.get("stdout"), str) + assert isinstance(ret.get("stderr"), str) + assert ( + ret.get("stderr").rstrip() == "cheese" + if not salt.utils.platform.is_windows() + else '"cheese"' + ) + + +@pytest.mark.slow_test +def test_retcode(cmdmod): + """ + cmd.retcode + """ + assert cmdmod.retcode("exit 0", python_shell=True) == 0 + assert cmdmod.retcode("exit 1", python_shell=True) == 1 + + +@pytest.mark.slow_test +def test_run_all_with_success_retcodes(cmdmod): + """ + cmd.run with success_retcodes + """ + ret = cmdmod.run_all("exit 42", success_retcodes=[42], python_shell=True) + + assert "retcode" in ret + assert ret.get("retcode") == 0 + + +@pytest.mark.slow_test +def test_retcode_with_success_retcodes(cmdmod): + """ + cmd.run with success_retcodes + """ + ret = cmdmod.retcode("exit 42", success_retcodes=[42], python_shell=True) + + assert ret == 0 + + +@pytest.mark.slow_test +def test_run_all_with_success_stderr(cmdmod, tmp_path): + """ + cmd.run with success_retcodes + """ + random_file = str(tmp_path / f"{random.random()}") + + if salt.utils.platform.is_windows(): + func = "type" + expected_stderr = "cannot find the file specified" + else: + func = "cat" + expected_stderr = "No such file or directory" + ret = cmdmod.run_all( + f"{func} {random_file}", + success_stderr=[expected_stderr], + python_shell=True, + ) + + assert "retcode" in ret + assert ret.get("retcode") == 0 + + +@pytest.mark.slow_test +def test_script(cmdmod, script_contents): + """ + cmd.script + """ + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = cmdmod.script(script, args, saltenv="base") + assert ret["stdout"] == args + + +@pytest.mark.slow_test +def test_script_query_string(cmdmod, script_contents): + """ + cmd.script + """ + args = "saltines crackers biscuits=yes" + script = "salt://script.py?saltenv=base" + ret = cmdmod.script(script, args, saltenv="base") + assert ret["stdout"] == args + + +@pytest.mark.slow_test +def test_script_retcode(cmdmod, script_contents): + """ + cmd.script_retcode + """ + script = "salt://script.py" + ret = cmdmod.script_retcode(script, saltenv="base") + assert ret == 0 + + +@pytest.mark.slow_test +def test_script_cwd(cmdmod, script_contents, tmp_path): + """ + cmd.script with cwd + """ + tmp_cwd = str(tmp_path) + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = cmdmod.script(script, args, cwd=tmp_cwd, saltenv="base") + assert ret["stdout"] == args + + +@pytest.mark.slow_test +def test_script_cwd_with_space(cmdmod, script_contents, tmp_path): + """ + cmd.script with cwd + """ + tmp_cwd = str(tmp_path / "test 2") + os.mkdir(tmp_cwd) + + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = cmdmod.script(script, args, cwd=tmp_cwd, saltenv="base") + assert ret["stdout"] == args + + +@pytest.mark.destructive_test +def test_tty(cmdmod): + """ + cmd.tty + """ + for tty in ("tty0", "pts3"): + if os.path.exists(os.path.join("/dev", tty)): + ret = cmdmod.tty(tty, "apply salt liberally") + assert "Success" in ret + + +@pytest.mark.skip_on_windows +@pytest.mark.skip_if_binaries_missing("which") +def test_which(cmdmod): + """ + cmd.which + """ + cmd_which = cmdmod.which("cat") + assert isinstance(cmd_which, str) + cmd_run = cmdmod.run("which cat") + assert isinstance(cmd_run, str) + assert cmd_which.rstrip() == cmd_run.rstrip() + + +@pytest.mark.skip_on_windows +@pytest.mark.skip_if_binaries_missing("which") +def test_which_bin(cmdmod): + """ + cmd.which_bin + """ + cmds = ["pip3", "pip2", "pip", "pip-python"] + ret = cmdmod.which_bin(cmds) + assert os.path.split(ret)[1] in cmds + + +@pytest.mark.slow_test +def test_has_exec(cmdmod, available_python_executable): + """ + cmd.has_exec + """ + assert cmdmod.has_exec(available_python_executable) + assert not cmdmod.has_exec("alllfsdfnwieulrrh9123857ygf") + + +@pytest.mark.slow_test +def test_exec_code(cmdmod, available_python_executable): + """ + cmd.exec_code + """ + code = dedent( + """ + import sys + sys.stdout.write('cheese') + """ + ) + assert cmdmod.exec_code(available_python_executable, code).rstrip() == "cheese" + + +@pytest.mark.slow_test +def test_exec_code_with_single_arg(cmdmod, available_python_executable): + """ + cmd.exec_code + """ + code = dedent( + """ + import sys + sys.stdout.write(sys.argv[1]) + """ + ) + arg = "cheese" + assert cmdmod.exec_code(available_python_executable, code, args=arg).rstrip() == arg + + +@pytest.mark.slow_test +def test_exec_code_with_multiple_args(cmdmod, available_python_executable): + """ + cmd.exec_code + """ + code = dedent( + """ + import sys + sys.stdout.write(sys.argv[1]) + """ + ) + arg = "cheese" + assert ( + cmdmod.exec_code(available_python_executable, code, args=[arg, "test"]).rstrip() + == arg + ) + + +@pytest.mark.slow_test +def test_quotes(cmdmod): + """ + cmd.run with quoted command + """ + cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ + expected_result = 'SELECT * FROM foo WHERE bar="baz"' + result = cmdmod.run_stdout(cmd).strip() + assert result == expected_result + + +@pytest.mark.skip_if_not_root +@pytest.mark.skip_on_windows(reason="Skip on Windows, requires password") +def test_quotes_runas(cmdmod, running_username): + """ + cmd.run with quoted command + """ + cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ + expected_result = 'SELECT * FROM foo WHERE bar="baz"' + result = cmdmod.run_all(cmd, runas=running_username) + errmsg = f"The command returned: {result}" + assert result["retcode"] == 0, errmsg + assert result["stdout"] == expected_result, errmsg + + +@pytest.mark.destructive_test +@pytest.mark.skip_if_not_root +@pytest.mark.skip_on_windows(reason="Skip on Windows, uses unix commands") +@pytest.mark.slow_test +def test_cwd_runas(cmdmod, usermod, runas_usr, tmp_path): + """ + cmd.run should be able to change working directory correctly, whether + or not runas is in use. + """ + cmd = "pwd" + tmp_cwd = str(tmp_path) + os.chmod(tmp_cwd, 0o711) + + cwd_normal = cmdmod.run_stdout(cmd, cwd=tmp_cwd).rstrip("\n") + assert tmp_cwd == cwd_normal + + with _ensure_user_exists(runas_usr, usermod): + cwd_runas = cmdmod.run_stdout(cmd, cwd=tmp_cwd, runas=runas_usr).rstrip("\n") + assert tmp_cwd == cwd_runas + + +@pytest.mark.destructive_test +@pytest.mark.skip_if_not_root +@pytest.mark.skip_unless_on_darwin(reason="Applicable to MacOS only") +@pytest.mark.slow_test +def test_runas_env(cmdmod, usermod, runas_usr): + """ + cmd.run should be able to change working directory correctly, whether + or not runas is in use. + """ + with _ensure_user_exists(runas_usr, usermod): + user_path = cmdmod.run_stdout('printf %s "$PATH"', runas=runas_usr) + # XXX: Not sure of a better way. Environment starts out with + # /bin:/usr/bin and should be populated by path helper and the bash + # profile. + assert "/bin:/usr/bin" != user_path + + +@pytest.mark.destructive_test +@pytest.mark.skip_if_not_root +@pytest.mark.skip_unless_on_darwin(reason="Applicable to MacOS only") +@pytest.mark.slow_test +def test_runas_complex_command_bad_cwd(cmdmod, usermod, runas_usr, tmp_path): + """ + cmd.run should not accidentally run parts of a complex command when + given a cwd which cannot be used by the user the command is run as. + Due to the need to use `su -l` to login to another user on MacOS, we + cannot cd into directories that the target user themselves does not + have execute permission for. To an extent, this test is testing that + buggy behaviour, but its purpose is to ensure that the greater bug of + running commands after failing to cd does not occur. + """ + tmp_cwd = str(tmp_path) + os.chmod(tmp_cwd, 0o700) + + with _ensure_user_exists(runas_usr, usermod): + cmd_result = cmdmod.run_all( + 'pwd; pwd; : $(echo "You have failed the test" >&2)', + cwd=tmp_cwd, + runas=runas_usr, + ) + + assert "" == cmd_result["stdout"] + assert "You have failed the test" not in cmd_result["stderr"] + assert 0 != cmd_result["retcode"] + + +@SKIP_INITIAL_PHOTONOS_FAILURES +@pytest.mark.skip_on_windows +@pytest.mark.skip_if_not_root +@pytest.mark.destructive_test +@pytest.mark.slow_test +def test_runas(cmdmod, usermod, runas_usr): + """ + Ensure that the env is the runas user's + """ + with _ensure_user_exists(runas_usr, usermod): + out = cmdmod.run("env", runas=runas_usr).splitlines() + assert f"USER={runas_usr}" in out + + +@pytest.mark.skip_if_binaries_missing("sleep", reason="sleep cmd not installed") +def test_timeout(cmdmod): + """ + cmd.run trigger timeout + """ + out = cmdmod.run("sleep 2 && echo hello", timeout=1, python_shell=True) + assert "Timed out" in out + + +@pytest.mark.skip_if_binaries_missing("sleep", reason="sleep cmd not installed") +def test_timeout_success(cmdmod): + """ + cmd.run sufficient timeout to succeed + """ + out = cmdmod.run("sleep 1 && echo hello", timeout=2, python_shell=True) + assert out == "hello" + + +@pytest.mark.slow_test +def test_cmd_run_whoami(cmdmod, running_username): + """ + test return of whoami + """ + if not salt.utils.platform.is_windows(): + user = running_username + else: + user = salt.utils.user.get_specific_user() + if user.startswith("sudo_"): + user = user.replace("sudo_", "") + cmd = cmdmod.run("whoami") + assert user.lower() == cmd.lower() + + +@pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") +@pytest.mark.slow_test +def test_windows_env_handling(cmdmod): + """ + Ensure that nt.environ is used properly with cmd.run* + """ + out = cmdmod.run("set", env={"abc": "123", "ABC": "456"}).splitlines() + assert "abc=123" in out + assert "ABC=456" in out + + +@pytest.mark.slow_test +@pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") +def test_windows_powershell_script_args(cmdmod, issue_56195_test_ps1): + """ + Ensure that powershell processes inline script in args + """ + val = "i like cheese" + args = ( + '-SecureString (ConvertTo-SecureString -String "{}" -AsPlainText -Force)' + " -ErrorAction Stop".format(val) + ) + script = "salt://issue_56195_test.ps1" + ret = cmdmod.script(script, args=args, shell="powershell", saltenv="base") + assert ret["stdout"] == val + + +@pytest.mark.slow_test +@pytest.mark.skip_unless_on_windows(reason="Minion is not Windows") +@pytest.mark.skip_if_binaries_missing("pwsh") +def test_windows_powershell_script_args_pwsh(cmdmod, issue_56195_test_ps1): + """ + Ensure that powershell processes inline script in args with powershell + core + """ + val = "i like cheese" + args = ( + '-SecureString (ConvertTo-SecureString -String "{}" -AsPlainText -Force)' + " -ErrorAction Stop".format(val) + ) + script = "salt://issue_56195_test.ps1" + ret = cmdmod.script(script, args=args, shell="pwsh", saltenv="base") + assert ret["stdout"] == val diff --git a/tests/pytests/functional/modules/test_dockermod.py b/tests/pytests/functional/modules/test_dockermod.py index 3c7bb25e461..a5b40869352 100644 --- a/tests/pytests/functional/modules/test_dockermod.py +++ b/tests/pytests/functional/modules/test_dockermod.py @@ -2,6 +2,7 @@ Integration tests for the docker_container states """ import logging +import os import pytest from saltfactories.utils import random_string @@ -11,9 +12,12 @@ pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("docker", "dockerd", check_all=False), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run inside a container"), ] diff --git a/tests/pytests/functional/modules/test_swarm.py b/tests/pytests/functional/modules/test_swarm.py index 8c0ce8cbd93..9dc70f5b3dc 100644 --- a/tests/pytests/functional/modules/test_swarm.py +++ b/tests/pytests/functional/modules/test_swarm.py @@ -1,10 +1,15 @@ +import os + import pytest import salt.utils.versions +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="No hwclock in a container"), ] # The swarm module need the docker-py library installed diff --git a/tests/pytests/functional/modules/test_system.py b/tests/pytests/functional/modules/test_system.py index 2dabaaebfad..3b669c46afd 100644 --- a/tests/pytests/functional/modules/test_system.py +++ b/tests/pytests/functional/modules/test_system.py @@ -9,9 +9,12 @@ import pytest import salt.utils.files +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.skip_unless_on_linux, pytest.mark.slow_test, + pytest.mark.skipif(INSIDE_CONTAINER, reason="No systemd in container."), ] log = logging.getLogger(__name__) diff --git a/tests/pytests/functional/pillar/hg_pillar/test_hg_pillar.py b/tests/pytests/functional/pillar/hg_pillar/test_hg_pillar.py index 183b002d8b2..44603d96f1d 100644 --- a/tests/pytests/functional/pillar/hg_pillar/test_hg_pillar.py +++ b/tests/pytests/functional/pillar/hg_pillar/test_hg_pillar.py @@ -60,6 +60,7 @@ def hg_setup_and_teardown(): @pytest.mark.skip_on_windows( reason="just testing if this or hgfs causes the issue with total crash" ) +@pytest.mark.skipif(not HAS_HG, reason="missing hglib library") def test_ext_pillar(hg_setup_and_teardown): data = hg_pillar.ext_pillar("*", None, hg_setup_and_teardown) assert data == {"testinfo": "info", "testinfo2": "info"} diff --git a/tests/pytests/functional/states/rabbitmq/test_cluster.py b/tests/pytests/functional/states/rabbitmq/test_cluster.py index f8b4bdc225e..210b22a2360 100644 --- a/tests/pytests/functional/states/rabbitmq/test_cluster.py +++ b/tests/pytests/functional/states/rabbitmq/test_cluster.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_cluster states """ import logging +import os import pytest @@ -13,11 +14,14 @@ pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/rabbitmq/test_plugin.py b/tests/pytests/functional/states/rabbitmq/test_plugin.py index e1b686e3365..f1191490536 100644 --- a/tests/pytests/functional/states/rabbitmq/test_plugin.py +++ b/tests/pytests/functional/states/rabbitmq/test_plugin.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_plugin states """ import logging +import os import pytest @@ -14,11 +15,14 @@ log = logging.getLogger(__name__) pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/rabbitmq/test_policy.py b/tests/pytests/functional/states/rabbitmq/test_policy.py index e5cee97cbc8..7ccf6a522e0 100644 --- a/tests/pytests/functional/states/rabbitmq/test_policy.py +++ b/tests/pytests/functional/states/rabbitmq/test_policy.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_policy states """ import logging +import os import pytest @@ -14,11 +15,14 @@ log = logging.getLogger(__name__) pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/rabbitmq/test_upstream.py b/tests/pytests/functional/states/rabbitmq/test_upstream.py index cfdad35aba6..c7bcf3b0d44 100644 --- a/tests/pytests/functional/states/rabbitmq/test_upstream.py +++ b/tests/pytests/functional/states/rabbitmq/test_upstream.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_user states """ import logging +import os import pytest @@ -13,11 +14,14 @@ log = logging.getLogger(__name__) pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/rabbitmq/test_user.py b/tests/pytests/functional/states/rabbitmq/test_user.py index 2f9b22d28d2..31723df7be8 100644 --- a/tests/pytests/functional/states/rabbitmq/test_user.py +++ b/tests/pytests/functional/states/rabbitmq/test_user.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_user states """ import logging +import os import pytest @@ -13,11 +14,14 @@ log = logging.getLogger(__name__) pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/rabbitmq/test_vhost.py b/tests/pytests/functional/states/rabbitmq/test_vhost.py index a648d41854f..d6ac6901a25 100644 --- a/tests/pytests/functional/states/rabbitmq/test_vhost.py +++ b/tests/pytests/functional/states/rabbitmq/test_vhost.py @@ -3,6 +3,7 @@ Integration tests for the rabbitmq_user states """ import logging +import os import pytest @@ -13,11 +14,14 @@ log = logging.getLogger(__name__) pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/test_docker_network.py b/tests/pytests/functional/states/test_docker_network.py index 16a78b13a4a..0da01ed8bac 100644 --- a/tests/pytests/functional/states/test_docker_network.py +++ b/tests/pytests/functional/states/test_docker_network.py @@ -1,5 +1,6 @@ import functools import logging +import os import random import pytest @@ -15,9 +16,13 @@ pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("docker", "dockerd", check_all=False), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/functional/states/test_pkg.py b/tests/pytests/functional/states/test_pkg.py index 0e82dc608ba..12318c996d1 100644 --- a/tests/pytests/functional/states/test_pkg.py +++ b/tests/pytests/functional/states/test_pkg.py @@ -64,7 +64,7 @@ def PKG_CAP_TARGETS(grains): _PKG_CAP_TARGETS = [] if grains["os_family"] == "Suse": if grains["os"] == "SUSE": - _PKG_CAP_TARGETS = [("perl(ZNC)", "znc-perl")] + _PKG_CAP_TARGETS = [("perl(Error)", "perl-Error")] if not _PKG_CAP_TARGETS: pytest.skip("Capability not provided") return _PKG_CAP_TARGETS @@ -856,8 +856,8 @@ def test_pkg_cap_003_installed_multipkg_with_version( This is a destructive test as it installs and then removes two packages """ target, realpkg = PKG_CAP_TARGETS[0] - version = latest_version(target) - realver = latest_version(realpkg) + version = modules.pkg.version(target) + realver = modules.pkg.version(realpkg) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. diff --git a/tests/pytests/integration/cli/test_syndic_eauth.py b/tests/pytests/integration/cli/test_syndic_eauth.py index 57e9c0a467a..218022b9e3c 100644 --- a/tests/pytests/integration/cli/test_syndic_eauth.py +++ b/tests/pytests/integration/cli/test_syndic_eauth.py @@ -1,4 +1,5 @@ import json +import os import pathlib import tempfile import time @@ -7,9 +8,11 @@ import pytest docker = pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" pytestmark = [ pytest.mark.core_test, + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/integration/daemons/test_memory_leak.py b/tests/pytests/integration/daemons/test_memory_leak.py index 1b782760418..8157091c44e 100644 --- a/tests/pytests/integration/daemons/test_memory_leak.py +++ b/tests/pytests/integration/daemons/test_memory_leak.py @@ -1,3 +1,4 @@ +import os import time from multiprocessing import Manager, Process @@ -8,6 +9,8 @@ pytestmark = [ pytest.mark.slow_test, ] +GITHUB_ACTIONS = bool(os.getenv("GITHUB_ACTIONS", False)) + @pytest.fixture def testfile_path(tmp_path): @@ -45,6 +48,7 @@ def file_add_delete_sls(testfile_path, base_env_state_tree_root_dir): @pytest.mark.skip_on_darwin(reason="MacOS is a spawning platform, won't work") +@pytest.mark.skipif(GITHUB_ACTIONS, reason="Test is failing in GitHub Actions") @pytest.mark.flaky(max_runs=4) def test_memory_leak(salt_cli, salt_minion, file_add_delete_sls): max_usg = None diff --git a/tests/pytests/integration/modules/test_cmdmod.py b/tests/pytests/integration/modules/test_cmdmod.py index 4e8ce5824ee..d9c326c3f0a 100644 --- a/tests/pytests/integration/modules/test_cmdmod.py +++ b/tests/pytests/integration/modules/test_cmdmod.py @@ -1,5 +1,11 @@ +import logging + import pytest +import salt.utils.user + +log = logging.getLogger(__name__) + @pytest.fixture(scope="module") def non_root_account(): @@ -7,6 +13,14 @@ def non_root_account(): yield account +@pytest.fixture +def running_username(): + """ + Return the username that is running the code. + """ + return salt.utils.user.get_user() + + @pytest.mark.skip_if_not_root def test_exec_code_all(salt_call_cli, non_root_account): ret = salt_call_cli.run( @@ -22,3 +36,82 @@ def test_long_stdout(salt_cli, salt_minion): ) assert ret.returncode == 0 assert len(ret.data.strip()) == len(echo_str) + + +@pytest.mark.skip_if_not_root +@pytest.mark.skip_on_windows(reason="Skip on Windows, uses unix commands") +def test_avoid_injecting_shell_code_as_root( + salt_call_cli, non_root_account, running_username +): + """ + cmd.run should execute the whole command as the "runas" user, not + running substitutions as root. + """ + cmd = "echo $(id -u)" + + ret = salt_call_cli.run("cmd.run_stdout", cmd) + root_id = ret.json + ret = salt_call_cli.run("cmd.run_stdout", cmd, runas=running_username) + runas_root_id = ret.json + + ret = salt_call_cli.run("cmd.run_stdout", cmd, runas=non_root_account.username) + user_id = ret.json + + assert user_id != root_id + assert user_id != runas_root_id + assert root_id == runas_root_id + + +@pytest.mark.slow_test +def test_blacklist_glob(salt_call_cli): + """ + cmd_blacklist_glob + """ + cmd = "bad_command --foo" + ret = salt_call_cli.run( + "cmd.run", + cmd, + ) + + assert ( + ret.stderr.rstrip() + == "Error running 'cmd.run': The shell command \"bad_command --foo\" is not permitted" + ) + + +@pytest.mark.slow_test +def test_hide_output(salt_call_cli): + """ + Test the hide_output argument + """ + ls_command = ( + ["ls", "/"] if not salt.utils.platform.is_windows() else ["dir", "c:\\"] + ) + + error_command = ["thiscommanddoesnotexist"] + + # cmd.run + ret = salt_call_cli.run("cmd.run", ls_command, hide_output=True) + assert ret.data == "" + + # cmd.shell + ret = salt_call_cli.run("cmd.shell", ls_command, hide_output=True) + assert ret.data == "" + + # cmd.run_stdout + ret = salt_call_cli.run("cmd.run_stdout", ls_command, hide_output=True) + assert ret.data == "" + + # cmd.run_stderr + ret = salt_call_cli.run("cmd.shell", error_command, hide_output=True) + assert ret.data == "" + + # cmd.run_all (command should have produced stdout) + ret = salt_call_cli.run("cmd.run_all", ls_command, hide_output=True) + assert ret.data["stdout"] == "" + assert ret.data["stderr"] == "" + + # cmd.run_all (command should have produced stderr) + ret = salt_call_cli.run("cmd.run_all", error_command, hide_output=True) + assert ret.data["stdout"] == "" + assert ret.data["stderr"] == "" diff --git a/tests/pytests/integration/modules/test_virt.py b/tests/pytests/integration/modules/test_virt.py index 57ec239c4e9..1b7f30154a7 100644 --- a/tests/pytests/integration/modules/test_virt.py +++ b/tests/pytests/integration/modules/test_virt.py @@ -2,6 +2,7 @@ Validate the virt module """ import logging +import os from numbers import Number from xml.etree import ElementTree @@ -14,9 +15,12 @@ docker = pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("docker"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/integration/ssh/test_log.py b/tests/pytests/integration/ssh/test_log.py index e87c4a8581f..683feb8bd91 100644 --- a/tests/pytests/integration/ssh/test_log.py +++ b/tests/pytests/integration/ssh/test_log.py @@ -2,6 +2,7 @@ Integration tests for salt-ssh logging """ import logging +import os import time import pytest @@ -11,12 +12,14 @@ from tests.support.helpers import Keys pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/integration/ssh/test_master.py b/tests/pytests/integration/ssh/test_master.py index 31e318870cb..0c2f482cf9f 100644 --- a/tests/pytests/integration/ssh/test_master.py +++ b/tests/pytests/integration/ssh/test_master.py @@ -2,6 +2,8 @@ Simple Smoke Tests for Connected SSH minions """ +import os + import pytest from saltfactories.utils.functional import StateResult @@ -10,7 +12,10 @@ pytestmark = [ pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"), ] +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + +@pytest.mark.skipif(INSIDE_CONTAINER, reason="No systemd in container.") @pytest.mark.skip_if_not_root def test_service(salt_ssh_cli, grains): service = "cron" diff --git a/tests/pytests/integration/ssh/test_py_versions.py b/tests/pytests/integration/ssh/test_py_versions.py index 52ab819e808..71d4cfaa94e 100644 --- a/tests/pytests/integration/ssh/test_py_versions.py +++ b/tests/pytests/integration/ssh/test_py_versions.py @@ -2,6 +2,7 @@ Integration tests for salt-ssh py_versions """ import logging +import os import socket import time @@ -12,12 +13,14 @@ from tests.support.helpers import Keys pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/integration/ssh/test_ssh_setup.py b/tests/pytests/integration/ssh/test_ssh_setup.py index eddf31caccd..79b55ad90a5 100644 --- a/tests/pytests/integration/ssh/test_ssh_setup.py +++ b/tests/pytests/integration/ssh/test_ssh_setup.py @@ -17,12 +17,14 @@ from tests.support.helpers import Keys pytest.importorskip("docker") +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd"), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/scenarios/compat/test_with_versions.py b/tests/pytests/scenarios/compat/test_with_versions.py index 75a2b87f24c..498dd6a60de 100644 --- a/tests/pytests/scenarios/compat/test_with_versions.py +++ b/tests/pytests/scenarios/compat/test_with_versions.py @@ -5,6 +5,7 @@ Test current salt master with older salt minions """ import logging +import os import pathlib import pytest @@ -18,6 +19,8 @@ docker = pytest.importorskip("docker") log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.slow_test, @@ -25,6 +28,7 @@ pytestmark = [ pytest.mark.skipif( salt.utils.platform.is_photonos() is True, reason="Skip on PhotonOS" ), + pytest.mark.skipif(INSIDE_CONTAINER, reason="Cannot run in a container"), ] diff --git a/tests/pytests/scenarios/failover/multimaster/test_failover_master.py b/tests/pytests/scenarios/failover/multimaster/test_failover_master.py index 6efecfb8334..9f6251a4d6f 100644 --- a/tests/pytests/scenarios/failover/multimaster/test_failover_master.py +++ b/tests/pytests/scenarios/failover/multimaster/test_failover_master.py @@ -12,7 +12,10 @@ pytestmark = [ log = logging.getLogger(__name__) +GITHUB_ACTIONS = bool(os.getenv("GITHUB_ACTIONS", False)) + +@pytest.mark.skipif(GITHUB_ACTIONS, reason="Test is failing in GitHub Actions") def test_pki(salt_mm_failover_master_1, salt_mm_failover_master_2, caplog): """ Verify https://docs.saltproject.io/en/latest/topics/tutorials/multimaster_pki.html diff --git a/tests/pytests/scenarios/setup/test_install.py b/tests/pytests/scenarios/setup/test_install.py index 48f1d5889f6..7664fda804e 100644 --- a/tests/pytests/scenarios/setup/test_install.py +++ b/tests/pytests/scenarios/setup/test_install.py @@ -3,6 +3,7 @@ Tests for building and installing salt """ import json import logging +import os import pathlib import re import sys @@ -16,11 +17,16 @@ from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES log = logging.getLogger(__name__) +INSIDE_CONTAINER = os.getenv("HOSTNAME", "") == "salt-test-container" + pytestmark = [ pytest.mark.core_test, pytest.mark.windows_whitelisted, pytest.mark.skip_initial_onedir_failure, pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False), + pytest.mark.skipif( + INSIDE_CONTAINER, reason="No gcc and python3-devel in container." + ), ] diff --git a/tests/pytests/unit/modules/test_aptpkg.py b/tests/pytests/unit/modules/test_aptpkg.py index eb72447c3aa..6f0b905ef73 100644 --- a/tests/pytests/unit/modules/test_aptpkg.py +++ b/tests/pytests/unit/modules/test_aptpkg.py @@ -1360,17 +1360,17 @@ def test_call_apt_dpkg_lock(): ] cmd_mock = MagicMock(side_effect=cmd_side_effect) - cmd_call = ( + cmd_call = [ call( ["dpkg", "-l", "python"], - env={}, - ignore_retcode=False, output_loglevel="quiet", python_shell=True, + env={}, + ignore_retcode=False, username="Darth Vader", ), - ) - expected_calls = [cmd_call * 5] + ] + expected_calls = cmd_call * 5 with patch.dict( aptpkg.__salt__, @@ -1390,7 +1390,7 @@ def test_call_apt_dpkg_lock(): # We should attempt to call the cmd 5 times assert cmd_mock.call_count == 5 - cmd_mock.has_calls(expected_calls) + cmd_mock.assert_has_calls(expected_calls) def test_services_need_restart_checkrestart_missing(): diff --git a/tests/pytests/unit/modules/test_linux_sysctl.py b/tests/pytests/unit/modules/test_linux_sysctl.py index 0bdd24039d7..6b0875bc460 100644 --- a/tests/pytests/unit/modules/test_linux_sysctl.py +++ b/tests/pytests/unit/modules/test_linux_sysctl.py @@ -215,7 +215,7 @@ def test_persist_no_conf_failure(): ): with pytest.raises(CommandExecutionError): linux_sysctl.persist("net.ipv4.ip_forward", 42, config=None) - fopen_mock.called_once() + fopen_mock.assert_called_once() def test_persist_no_conf_success(): @@ -353,7 +353,7 @@ def test_persist_value_with_spaces_already_set(tmp_path): """ config = str(tmp_path / "existing_sysctl_with_spaces.conf") value = "|/usr/share/kdump-tools/dump-core %p %s %t %e" - config_file_content = "kernel.core_pattern = {}\n".format(value) + config_file_content = f"kernel.core_pattern = {value}\n" with fopen(config, "w", encoding="utf-8") as config_file: config_file.write(config_file_content) mock_run = MagicMock(return_value=value) @@ -383,7 +383,7 @@ def test_persist_value_with_spaces_already_configured(tmp_path): """ config = str(tmp_path / "existing_sysctl_with_spaces.conf") value = "|/usr/share/kdump-tools/dump-core %p %s %t %e" - config_file_content = "kernel.core_pattern = {}\n".format(value) + config_file_content = f"kernel.core_pattern = {value}\n" with fopen(config, "w", encoding="utf-8") as config_file: config_file.write(config_file_content) mock_run = MagicMock(return_value="") @@ -451,7 +451,7 @@ def test_persist_value_with_spaces_update_config(tmp_path): assert os.path.isfile(config) with fopen(config, encoding="utf-8") as config_file: written = config_file.read() - assert written == "kernel.core_pattern = {}\n".format(value) + assert written == f"kernel.core_pattern = {value}\n" def test_persist_value_with_spaces_new_file(tmp_path): diff --git a/tests/pytests/unit/modules/test_win_ip.py b/tests/pytests/unit/modules/test_win_ip.py index 38eb6b1ac5f..94a3fe7ca93 100644 --- a/tests/pytests/unit/modules/test_win_ip.py +++ b/tests/pytests/unit/modules/test_win_ip.py @@ -151,7 +151,7 @@ def test_enable(): ): assert win_ip.enable("Ethernet") - mock_cmd.called_once_with( + mock_cmd.assert_called_once_with( [ "netsh", "interface", @@ -180,7 +180,7 @@ def test_disable(): ): assert win_ip.disable("Ethernet") - mock_cmd.called_once_with( + mock_cmd.assert_called_once_with( [ "netsh", "interface", diff --git a/tests/pytests/unit/test_master.py b/tests/pytests/unit/test_master.py index d338307d1f8..679229066d4 100644 --- a/tests/pytests/unit/test_master.py +++ b/tests/pytests/unit/test_master.py @@ -61,7 +61,7 @@ def test_fileserver_duration(): end = time.time() # Interval is equal to timeout so the _do_update method will be called # one time. - update.called_once() + update.assert_called_once() # Timeout is 1 second duration = end - start if duration > 2 and salt.utils.platform.spawning_platform(): diff --git a/tests/pytests/unit/test_minion.py b/tests/pytests/unit/test_minion.py index 740743194e4..a9e91742a2d 100644 --- a/tests/pytests/unit/test_minion.py +++ b/tests/pytests/unit/test_minion.py @@ -655,7 +655,9 @@ def test_gen_modules_executors(minion_opts): with patch("salt.pillar.get_pillar", return_value=MockPillarCompiler()): with patch("salt.loader.executors") as execmock: minion.gen_modules() - assert execmock.called_with(minion.opts, minion.functions) + execmock.assert_called_with( + minion.opts, functions=minion.functions, proxy=minion.proxy, context={} + ) finally: minion.destroy() diff --git a/tests/pytests/unit/utils/event/test_event.py b/tests/pytests/unit/utils/event/test_event.py index e289e72dad0..f4b6c159996 100644 --- a/tests/pytests/unit/utils/event/test_event.py +++ b/tests/pytests/unit/utils/event/test_event.py @@ -38,7 +38,7 @@ def sock_dir(tmp_path): def _assert_got_event(evt, data, msg=None, expected_failure=False): assert evt is not None, msg for key in data: - assert key in evt, "{}: Key {} missing".format(msg, key) + assert key in evt, f"{msg}: Key {key} missing" assertMsg = "{0}: Key {1} value mismatch, {2} != {3}" assertMsg = assertMsg.format(msg, key, data[key], evt[key]) if not expected_failure: @@ -59,8 +59,8 @@ def test_minion_event(sock_dir): :10 ] with salt.utils.event.MinionEvent(opts, listen=False) as me: - assert me.puburi == str(sock_dir / "minion_event_{}_pub.ipc".format(id_hash)) - assert me.pulluri == str(sock_dir / "minion_event_{}_pull.ipc".format(id_hash)) + assert me.puburi == str(sock_dir / f"minion_event_{id_hash}_pub.ipc") + assert me.pulluri == str(sock_dir / f"minion_event_{id_hash}_pull.ipc") def test_minion_event_tcp_ipc_mode(): @@ -73,8 +73,8 @@ def test_minion_event_tcp_ipc_mode(): def test_minion_event_no_id(sock_dir): with salt.utils.event.MinionEvent(dict(sock_dir=str(sock_dir)), listen=False) as me: id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes("")).hexdigest()[:10] - assert me.puburi == str(sock_dir / "minion_event_{}_pub.ipc".format(id_hash)) - assert me.pulluri == str(sock_dir / "minion_event_{}_pull.ipc".format(id_hash)) + assert me.puburi == str(sock_dir / f"minion_event_{id_hash}_pub.ipc") + assert me.pulluri == str(sock_dir / f"minion_event_{id_hash}_pull.ipc") @pytest.mark.slow_test @@ -256,9 +256,9 @@ def test_event_many(sock_dir): with eventpublisher_process(str(sock_dir)): with salt.utils.event.MasterEvent(str(sock_dir), listen=True) as me: for i in range(500): - me.fire_event({"data": "{}".format(i)}, "testevents") + me.fire_event({"data": f"{i}"}, "testevents") evt = me.get_event(tag="testevents") - _assert_got_event(evt, {"data": "{}".format(i)}, "Event {}".format(i)) + _assert_got_event(evt, {"data": f"{i}"}, f"Event {i}") @pytest.mark.slow_test @@ -268,10 +268,10 @@ def test_event_many_backlog(sock_dir): with salt.utils.event.MasterEvent(str(sock_dir), listen=True) as me: # Must not exceed zmq HWM for i in range(500): - me.fire_event({"data": "{}".format(i)}, "testevents") + me.fire_event({"data": f"{i}"}, "testevents") for i in range(500): evt = me.get_event(tag="testevents") - _assert_got_event(evt, {"data": "{}".format(i)}, "Event {}".format(i)) + _assert_got_event(evt, {"data": f"{i}"}, f"Event {i}") # Test the fire_master function. As it wraps the underlying fire_event, @@ -300,7 +300,7 @@ def test_connect_pull_should_debug_log_on_StreamClosedError(): event = SaltEvent(node=None) with patch.object(event, "pusher") as mock_pusher: with patch.object( - salt.utils.event.log, "debug", auto_spec=True + salt.utils.event.log, "debug", autospec=True ) as mock_log_debug: mock_pusher.connect.side_effect = ( salt.ext.tornado.iostream.StreamClosedError @@ -317,10 +317,10 @@ def test_connect_pull_should_error_log_on_other_errors(error): event = SaltEvent(node=None) with patch.object(event, "pusher") as mock_pusher: with patch.object( - salt.utils.event.log, "debug", auto_spec=True + salt.utils.event.log, "debug", autospec=True ) as mock_log_debug: with patch.object( - salt.utils.event.log, "error", auto_spec=True + salt.utils.event.log, "error", autospec=True ) as mock_log_error: mock_pusher.connect.side_effect = error event.connect_pull() diff --git a/tests/unit/modules/test_boto_apigateway.py b/tests/unit/modules/test_boto_apigateway.py index 5f3d2a49822..ebf50679bd8 100644 --- a/tests/unit/modules/test_boto_apigateway.py +++ b/tests/unit/modules/test_boto_apigateway.py @@ -15,6 +15,7 @@ from tests.support.unit import TestCase # pylint: disable=import-error,no-name-in-module try: + import boto import boto3 import botocore from botocore.exceptions import ClientError diff --git a/tests/unit/modules/test_boto_cognitoidentity.py b/tests/unit/modules/test_boto_cognitoidentity.py index 1e213a169ac..974832f9ff9 100644 --- a/tests/unit/modules/test_boto_cognitoidentity.py +++ b/tests/unit/modules/test_boto_cognitoidentity.py @@ -14,6 +14,7 @@ from tests.support.unit import TestCase # pylint: disable=import-error,no-name-in-module try: + import boto import boto3 from botocore.exceptions import ClientError diff --git a/tests/unit/modules/test_boto_elasticsearch_domain.py b/tests/unit/modules/test_boto_elasticsearch_domain.py index 5c5845aa25b..0578a81e8ef 100644 --- a/tests/unit/modules/test_boto_elasticsearch_domain.py +++ b/tests/unit/modules/test_boto_elasticsearch_domain.py @@ -14,6 +14,7 @@ from tests.support.unit import TestCase # pylint: disable=import-error,no-name-in-module try: + import boto import boto3 from botocore.exceptions import ClientError diff --git a/tests/unit/modules/test_boto_lambda.py b/tests/unit/modules/test_boto_lambda.py index d32dc9345b6..ecaa532f1ff 100644 --- a/tests/unit/modules/test_boto_lambda.py +++ b/tests/unit/modules/test_boto_lambda.py @@ -18,6 +18,7 @@ from tests.support.unit import TestCase # pylint: disable=import-error,no-name-in-module try: + import boto import boto3 from botocore import __version__ as found_botocore_version from botocore.exceptions import ClientError diff --git a/tests/unit/modules/test_network.py b/tests/unit/modules/test_network.py index 34b06250fc6..9eef9a02f58 100644 --- a/tests/unit/modules/test_network.py +++ b/tests/unit/modules/test_network.py @@ -153,9 +153,11 @@ class NetworkTestCase(TestCase, LoaderModuleMockMixin): """ Test for Performs a DNS lookup with dig """ - with patch("salt.utils.path.which", MagicMock(return_value="dig")), patch.dict( + with patch.dict( network.__utils__, {"network.sanitize_host": MagicMock(return_value="A")} - ), patch.dict(network.__salt__, {"cmd.run": MagicMock(return_value="A")}): + ), patch("salt.utils.path.which", MagicMock(return_value="dig")), patch.dict( + network.__salt__, {"cmd.run": MagicMock(return_value="A")} + ): self.assertEqual(network.dig("host"), "A") def test_arp(self): diff --git a/tests/unit/modules/test_nilrt_ip.py b/tests/unit/modules/test_nilrt_ip.py index 1261473edb4..50dc13b20b8 100644 --- a/tests/unit/modules/test_nilrt_ip.py +++ b/tests/unit/modules/test_nilrt_ip.py @@ -28,7 +28,7 @@ class NilrtIPTestCase(TestCase, LoaderModuleMockMixin): "salt.modules.nilrt_ip._change_dhcp_config", return_value=True ) as change_dhcp_config_mock: assert nilrt_ip._change_state("test_interface", "down") - assert change_dhcp_config_mock.called_with("test_interface", False) + change_dhcp_config_mock.assert_called_with("test_interface", False) def test_change_state_up_state(self): """ @@ -42,7 +42,7 @@ class NilrtIPTestCase(TestCase, LoaderModuleMockMixin): "salt.modules.nilrt_ip._change_dhcp_config", return_value=True ) as change_dhcp_config_mock: assert nilrt_ip._change_state("test_interface", "up") - assert change_dhcp_config_mock.called_with("test_interface") + change_dhcp_config_mock.assert_called_with("test_interface") def test_set_static_all_with_dns(self): """ diff --git a/tests/unit/modules/test_zcbuildout.py b/tests/unit/modules/test_zcbuildout.py index f793e3fc3f8..5a5996e110e 100644 --- a/tests/unit/modules/test_zcbuildout.py +++ b/tests/unit/modules/test_zcbuildout.py @@ -451,6 +451,7 @@ class BuildoutOnlineTestCase(Base): ) @pytest.mark.slow_test + @pytest.mark.skip(reason="TODO this test should probably be fixed") def test_run_buildout(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): self.skipTest( @@ -467,6 +468,7 @@ class BuildoutOnlineTestCase(Base): self.assertTrue("Installing b" in out) @pytest.mark.slow_test + @pytest.mark.skip(reason="TODO this test should probably be fixed") def test_buildout(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): self.skipTest( diff --git a/tests/unit/netapi/rest_tornado/test_saltnado.py b/tests/unit/netapi/rest_tornado/test_saltnado.py index 7b63a65d4f3..c4758e700ab 100644 --- a/tests/unit/netapi/rest_tornado/test_saltnado.py +++ b/tests/unit/netapi/rest_tornado/test_saltnado.py @@ -647,7 +647,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.dict( self.handler.application.opts, @@ -698,7 +697,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.object( self.handler, @@ -729,8 +727,8 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): { "tag": "fnord", "data": { - "return": "return from fnord {}".format(i), - "id": "fnord {}".format(i), + "return": f"return from fnord {i}", + "id": f"fnord {i}", }, } ) @@ -760,7 +758,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.object( self.handler, @@ -794,8 +791,8 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): { "tag": "fnord", "data": { - "return": "return from fnord {}".format(i), - "id": "fnord {}".format(i), + "return": f"return from fnord {i}", + "id": f"fnord {i}", }, } ) @@ -820,7 +817,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.dict( self.handler.application.opts, @@ -843,12 +839,12 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): completed_events = [salt.ext.tornado.gen.Future() for _ in range(10)] events_by_id = {} for i, event in enumerate(completed_events): - id_ = "fnord {}".format(i) + id_ = f"fnord {i}" events_by_id[id_] = event event.set_result( { "tag": "fnord", - "data": {"return": "return from {}".format(id_), "id": id_}, + "data": {"return": f"return from {id_}", "id": id_}, } ) expected_result = { @@ -878,7 +874,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.dict( self.handler.application.opts, @@ -904,12 +899,12 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): events_by_id = {} # Setup some real-enough looking return data for i, event in enumerate(completed_events): - id_ = "fnord {}".format(i) + id_ = f"fnord {i}" events_by_id[id_] = event event.set_result( { "tag": "fnord", - "data": {"return": "return from {}".format(id_), "id": id_}, + "data": {"return": f"return from {id_}", "id": id_}, } ) # Hard coded instead of dynamic to avoid potentially writing a test @@ -971,7 +966,6 @@ class TestDisbatchLocal(salt.ext.tornado.testing.AsyncTestCase): with patch.object( self.handler.application.event_listener, "get_event", - autospec=True, side_effect=fancy_get_event, ), patch.object( self.handler, diff --git a/tests/unit/states/test_boto_apigateway.py b/tests/unit/states/test_boto_apigateway.py index 51c85d6058a..1edde8d303c 100644 --- a/tests/unit/states/test_boto_apigateway.py +++ b/tests/unit/states/test_boto_apigateway.py @@ -20,6 +20,7 @@ from tests.support.unit import TestCase from tests.unit.modules.test_boto_apigateway import BotoApiGatewayTestCaseMixin try: + import boto import boto3 import botocore from botocore.exceptions import ClientError diff --git a/tests/unit/states/test_boto_cognitoidentity.py b/tests/unit/states/test_boto_cognitoidentity.py index 4354df0546f..479477ac800 100644 --- a/tests/unit/states/test_boto_cognitoidentity.py +++ b/tests/unit/states/test_boto_cognitoidentity.py @@ -18,6 +18,7 @@ from tests.unit.modules.test_boto_cognitoidentity import ( ) try: + import boto import boto3 from botocore.exceptions import ClientError diff --git a/tests/unit/states/test_zcbuildout.py b/tests/unit/states/test_zcbuildout.py index db6013076d1..0abaadeb4be 100644 --- a/tests/unit/states/test_zcbuildout.py +++ b/tests/unit/states/test_zcbuildout.py @@ -48,6 +48,7 @@ class BuildoutTestCase(Base): self.assertFalse(ret["result"]) @pytest.mark.slow_test + @pytest.mark.skip(reason="TODO this test should probably be fixed") def test_installed(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): self.skipTest( -- 2.43.0