123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- # -*- coding: utf-8 -*-
- """
- :codeauthor: Pedro Algarvio (pedro@algarvio.me)
- tests.integration.shell.call
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- """
- from __future__ import absolute_import
- import logging
- import os
- import re
- import shutil
- import sys
- import pytest
- import salt.utils.files
- import salt.utils.json
- import salt.utils.platform
- import salt.utils.yaml
- from salt.ext import six
- from tests.integration.utils import testprogram
- from tests.support.case import ShellCase
- from tests.support.helpers import change_cwd, flaky, slowTest, with_tempfile
- from tests.support.mixins import ShellCaseCommonTestsMixin
- from tests.support.runtests import RUNTIME_VARS
- from tests.support.unit import skipIf
- log = logging.getLogger(__name__)
- @pytest.mark.windows_whitelisted
- class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin):
- _call_binary_ = "salt-call"
- @slowTest
- def test_default_output(self):
- out = self.run_call("-l quiet test.fib 3")
- expect = ["local:", " - 2"]
- self.assertEqual(expect, out[:-1])
- @slowTest
- def test_text_output(self):
- out = self.run_call("-l quiet --out txt test.fib 3")
- expect = ["local: (2"]
- self.assertEqual("".join(expect), "".join(out).rsplit(",", 1)[0])
- @slowTest
- def test_json_out_indent(self):
- out = self.run_call("test.ping -l quiet --out=json --out-indent=-1")
- self.assertIn('"local": true', "".join(out))
- out = self.run_call("test.ping -l quiet --out=json --out-indent=0")
- self.assertIn('"local": true', "".join(out))
- out = self.run_call("test.ping -l quiet --out=json --out-indent=1")
- self.assertIn('"local": true', "".join(out))
- @slowTest
- def test_local_sls_call(self):
- fileroot = os.path.join(RUNTIME_VARS.FILES, "file", "base")
- out = self.run_call(
- "--file-root {0} state.sls saltcalllocal".format(fileroot), local=True
- )
- self.assertIn("Name: test.echo", "".join(out))
- self.assertIn("Result: True", "".join(out))
- self.assertIn("hello", "".join(out))
- self.assertIn("Succeeded: 1", "".join(out))
- @with_tempfile()
- @slowTest
- def test_local_salt_call(self, name):
- """
- This tests to make sure that salt-call does not execute the
- function twice, see https://github.com/saltstack/salt/pull/49552
- """
- def _run_call(cmd):
- cmd = "--out=json " + cmd
- return salt.utils.json.loads("".join(self.run_call(cmd, local=True)))[
- "local"
- ]
- ret = _run_call('state.single file.append name={0} text="foo"'.format(name))
- ret = ret[next(iter(ret))]
- # Make sure we made changes
- assert ret["changes"]
- # 2nd sanity check: make sure that "foo" only exists once in the file
- with salt.utils.files.fopen(name) as fp_:
- contents = fp_.read()
- assert contents.count("foo") == 1, contents
- @skipIf(
- salt.utils.platform.is_windows() or salt.utils.platform.is_darwin(),
- "This test requires a supported master",
- )
- @slowTest
- def test_user_delete_kw_output(self):
- ret = self.run_call("-l quiet -d user.delete")
- assert "salt '*' user.delete name remove=True force=True" in "".join(ret)
- @slowTest
- def test_salt_documentation_too_many_arguments(self):
- """
- Test to see if passing additional arguments shows an error
- """
- data = self.run_call("-d virtualenv.create /tmp/ve", catch_stderr=True)
- self.assertIn(
- "You can only get documentation for one method at one time",
- "\n".join(data[1]),
- )
- @slowTest
- def test_issue_6973_state_highstate_exit_code(self):
- """
- If there is no tops/master_tops or state file matches
- for this minion, salt-call should exit non-zero if invoked with
- option --retcode-passthrough
- """
- src = os.path.join(RUNTIME_VARS.BASE_FILES, "top.sls")
- dst = os.path.join(RUNTIME_VARS.BASE_FILES, "top.sls.bak")
- shutil.move(src, dst)
- expected_comment = "No states found for this minion"
- try:
- stdout, retcode = self.run_call(
- "-l quiet --retcode-passthrough state.highstate", with_retcode=True
- )
- finally:
- shutil.move(dst, src)
- self.assertIn(expected_comment, "".join(stdout))
- self.assertNotEqual(0, retcode)
- @skipIf(sys.platform.startswith("win"), "This test does not apply on Win")
- @skipIf(True, "to be re-enabled when #23623 is merged")
- def test_return(self):
- self.run_call('cmd.run "echo returnTOmaster"')
- jobs = [a for a in self.run_run("jobs.list_jobs")]
- self.assertTrue(True in ["returnTOmaster" in j for j in jobs])
- # lookback jid
- first_match = [(i, j) for i, j in enumerate(jobs) if "returnTOmaster" in j][0]
- jid, idx = None, first_match[0]
- while idx > 0:
- jid = re.match("([0-9]+):", jobs[idx])
- if jid:
- jid = jid.group(1)
- break
- idx -= 1
- assert idx > 0
- assert jid
- master_out = [a for a in self.run_run("jobs.lookup_jid {0}".format(jid))]
- self.assertTrue(True in ["returnTOmaster" in a for a in master_out])
- @skipIf(salt.utils.platform.is_windows(), "Skip on Windows")
- @slowTest
- def test_syslog_file_not_found(self):
- """
- test when log_file is set to a syslog file that does not exist
- """
- config_dir = os.path.join(RUNTIME_VARS.TMP, "log_file_incorrect")
- if not os.path.isdir(config_dir):
- os.makedirs(config_dir)
- with change_cwd(config_dir):
- with salt.utils.files.fopen(
- self.get_config_file_path("minion"), "r"
- ) as fh_:
- minion_config = salt.utils.yaml.load(fh_.read())
- minion_config["log_file"] = "file:///dev/doesnotexist"
- with salt.utils.files.fopen(
- os.path.join(config_dir, "minion"), "w"
- ) as fh_:
- fh_.write(
- salt.utils.yaml.dump(minion_config, default_flow_style=False)
- )
- ret = self.run_script(
- "salt-call",
- '--config-dir {0} cmd.run "echo foo"'.format(config_dir),
- timeout=120,
- catch_stderr=True,
- with_retcode=True,
- )
- try:
- if sys.version_info >= (3, 5, 4):
- self.assertIn("local:", ret[0])
- self.assertIn(
- "[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.",
- ret[1],
- )
- self.assertEqual(ret[2], 0)
- else:
- self.assertIn(
- "Failed to setup the Syslog logging handler", "\n".join(ret[1])
- )
- self.assertEqual(ret[2], 2)
- finally:
- if os.path.isdir(config_dir):
- shutil.rmtree(config_dir)
- @skipIf(True, "This test is unreliable. Need to investigate why more deeply.")
- @flaky
- def test_issue_15074_output_file_append(self):
- output_file_append = os.path.join(RUNTIME_VARS.TMP, "issue-15074")
- try:
- # Let's create an initial output file with some data
- _ = self.run_script(
- "salt-call",
- "-c {0} --output-file={1} test.versions".format(
- RUNTIME_VARS.TMP_MINION_CONF_DIR, output_file_append
- ),
- catch_stderr=True,
- with_retcode=True,
- )
- with salt.utils.files.fopen(output_file_append) as ofa:
- output = ofa.read()
- self.run_script(
- "salt-call",
- "-c {0} --output-file={1} --output-file-append test.versions".format(
- self.config_dir, output_file_append
- ),
- catch_stderr=True,
- with_retcode=True,
- )
- with salt.utils.files.fopen(output_file_append) as ofa:
- self.assertEqual(ofa.read(), output + output)
- finally:
- if os.path.exists(output_file_append):
- os.unlink(output_file_append)
- @skipIf(True, "This test is unreliable. Need to investigate why more deeply.")
- @flaky
- def test_issue_14979_output_file_permissions(self):
- output_file = os.path.join(RUNTIME_VARS.TMP, "issue-14979")
- with salt.utils.files.set_umask(0o077):
- try:
- # Let's create an initial output file with some data
- self.run_script(
- "salt-call",
- "-c {0} --output-file={1} -l trace -g".format(
- RUNTIME_VARS.TMP_MINION_CONF_DIR, output_file
- ),
- catch_stderr=True,
- with_retcode=True,
- )
- try:
- stat1 = os.stat(output_file)
- except OSError:
- self.fail("Failed to generate output file, see log for details")
- # Let's change umask
- os.umask(0o777) # pylint: disable=blacklisted-function
- self.run_script(
- "salt-call",
- "-c {0} --output-file={1} --output-file-append -g".format(
- RUNTIME_VARS.TMP_MINION_CONF_DIR, output_file
- ),
- catch_stderr=True,
- with_retcode=True,
- )
- try:
- stat2 = os.stat(output_file)
- except OSError:
- self.fail("Failed to generate output file, see log for details")
- self.assertEqual(stat1.st_mode, stat2.st_mode)
- # Data was appeneded to file
- self.assertTrue(stat1.st_size < stat2.st_size)
- # Let's remove the output file
- os.unlink(output_file)
- # Not appending data
- self.run_script(
- "salt-call",
- "-c {0} --output-file={1} -g".format(
- RUNTIME_VARS.TMP_MINION_CONF_DIR, output_file
- ),
- catch_stderr=True,
- with_retcode=True,
- )
- try:
- stat3 = os.stat(output_file)
- except OSError:
- self.fail("Failed to generate output file, see log for details")
- # Mode must have changed since we're creating a new log file
- self.assertNotEqual(stat1.st_mode, stat3.st_mode)
- finally:
- if os.path.exists(output_file):
- os.unlink(output_file)
- @skipIf(sys.platform.startswith("win"), "This test does not apply on Win")
- @slowTest
- def test_42116_cli_pillar_override(self):
- ret = self.run_call(
- "state.apply issue-42116-cli-pillar-override "
- 'pillar=\'{"myhost": "localhost"}\''
- )
- for line in ret:
- line = line.lstrip()
- if line == 'Comment: Command "ping -c 2 localhost" run':
- # Successful test
- break
- else:
- log.debug("salt-call output:\n\n%s", "\n".join(ret))
- self.fail("CLI pillar override not found in pillar data")
- @slowTest
- def test_pillar_items_masterless(self):
- """
- Test to ensure we get expected output
- from pillar.items with salt-call
- """
- get_items = self.run_call("pillar.items", local=True)
- exp_out = [
- " - Lancelot",
- " - Galahad",
- " - Bedevere",
- " monty:",
- " python",
- ]
- for out in exp_out:
- self.assertIn(out, get_items)
- def tearDown(self):
- """
- Teardown method to remove installed packages
- """
- user = ""
- user_info = self.run_call(" grains.get username", local=True)
- if (
- user_info
- and isinstance(user_info, (list, tuple))
- and isinstance(user_info[-1], six.string_types)
- ):
- user = user_info[-1].strip()
- super(CallTest, self).tearDown()
- @slowTest
- def test_exit_status_unknown_argument(self):
- """
- Ensure correct exit status when an unknown argument is passed to salt-call.
- """
- call = testprogram.TestProgramSaltCall(
- name="unknown_argument", parent_dir=self._test_dir,
- )
- # Call setup here to ensure config and script exist
- call.setup()
- stdout, stderr, status = call.run(
- args=["--unknown-argument"], catch_stderr=True, with_retcode=True,
- )
- self.assert_exit_status(
- status, "EX_USAGE", message="unknown argument", stdout=stdout, stderr=stderr
- )
- @slowTest
- def test_masterless_highstate(self):
- """
- test state.highstate in masterless mode
- """
- ret = self.run_call("state.highstate", local=True)
- destpath = os.path.join(RUNTIME_VARS.TMP, "testfile")
- exp_out = [
- " Function: file.managed",
- " Result: True",
- " ID: {0}".format(destpath),
- ]
- for out in exp_out:
- self.assertIn(out, ret)
- self.assertTrue(os.path.exists(destpath))
- @slowTest
- def test_exit_status_correct_usage(self):
- """
- Ensure correct exit status when salt-call starts correctly.
- """
- call = testprogram.TestProgramSaltCall(
- name="correct_usage", parent_dir=self._test_dir,
- )
- # Call setup here to ensure config and script exist
- call.setup()
- stdout, stderr, status = call.run(
- args=["--local", "test.true"], catch_stderr=True, with_retcode=True,
- )
- self.assert_exit_status(
- status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr
- )
|