salt/tests/integration/modules/test_cmdmod.py

383 lines
13 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
# Import python libs
from __future__ import absolute_import, print_function, unicode_literals
2012-03-27 23:05:38 +00:00
import os
import sys
import tempfile
2015-03-02 02:54:22 +00:00
import textwrap
2013-03-03 06:55:19 +00:00
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.helpers import (
2015-03-02 02:54:22 +00:00
destructiveTest,
skip_if_binaries_missing,
2018-05-06 16:54:55 +00:00
skip_if_not_root,
this_user,
2015-03-02 02:54:22 +00:00
)
from tests.support.paths import TMP
2018-03-30 18:12:02 +00:00
from tests.support.unit import skipIf
# Import salt libs
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
import salt.utils.path
2018-03-30 18:12:02 +00:00
import salt.utils.platform
2017-03-06 13:53:19 +00:00
# Import 3rd-party libs
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
from salt.ext import six
2017-03-06 13:53:19 +00:00
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
AVAILABLE_PYTHON_EXECUTABLE = salt.utils.path.which_bin([
'python',
'python2',
'python2.6',
'python2.7'
])
2012-03-27 23:05:38 +00:00
class CMDModuleTest(ModuleCase):
2012-03-27 23:05:38 +00:00
'''
Validate the cmd module
'''
def setUp(self):
2018-08-03 19:32:15 +00:00
self.runas_usr = 'nobody'
if salt.utils.platform.is_darwin():
self.runas_usr = 'macsalttest'
def tearDown(self):
if self._testMethodName == 'test_runas':
if salt.utils.platform.is_darwin():
if self.runas_usr in self.run_function('user.info', [self.runas_usr]).values():
self.run_function('user.delete', [self.runas_usr], remove=True)
2012-03-27 23:05:38 +00:00
def test_run(self):
'''
cmd.run
'''
2013-08-10 18:14:10 +00:00
shell = os.environ.get('SHELL')
if shell is None:
# Failed to get the SHELL var, don't run
2013-08-10 18:50:31 +00:00
self.skipTest('Unable to get the SHELL environment variable')
2012-03-27 23:05:38 +00:00
self.assertTrue(self.run_function('cmd.run', ['echo $SHELL']))
self.assertEqual(
2013-03-21 22:22:29 +00:00
self.run_function('cmd.run',
['echo $SHELL',
2015-02-23 22:04:43 +00:00
'shell={0}'.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')
2012-03-27 23:05:38 +00:00
def test_stdout(self):
'''
cmd.run_stdout
'''
2013-03-21 22:22:29 +00:00
self.assertEqual(self.run_function('cmd.run_stdout',
['echo "cheese"']).rstrip(),
'cheese' if not salt.utils.platform.is_windows() else '"cheese"')
2012-03-27 23:05:38 +00:00
def test_stderr(self):
'''
cmd.run_stderr
'''
2014-09-04 08:23:59 +00:00
if sys.platform.startswith(('freebsd', 'openbsd')):
shell = '/bin/sh'
else:
shell = '/bin/bash'
2013-03-21 22:22:29 +00:00
self.assertEqual(self.run_function('cmd.run_stderr',
['echo "cheese" 1>&2',
2014-12-23 20:02:56 +00:00
'shell={0}'.format(shell)], python_shell=True
).rstrip(),
'cheese' if not salt.utils.platform.is_windows() else '"cheese"')
2012-03-27 23:05:38 +00:00
def test_run_all(self):
'''
cmd.run_all
'''
2014-09-04 08:23:59 +00:00
if sys.platform.startswith(('freebsd', 'openbsd')):
shell = '/bin/sh'
else:
shell = '/bin/bash'
ret = self.run_function('cmd.run_all', ['echo "cheese" 1>&2',
2014-12-23 20:02:56 +00:00
'shell={0}'.format(shell)], python_shell=True)
2012-03-27 23:05:38 +00:00
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))
2017-03-06 13:53:19 +00:00
self.assertTrue(isinstance(ret.get('stdout'), six.string_types))
self.assertTrue(isinstance(ret.get('stderr'), six.string_types))
self.assertEqual(ret.get('stderr').rstrip(), 'cheese' if not salt.utils.platform.is_windows() else '"cheese"')
2012-03-27 23:05:38 +00:00
def test_retcode(self):
'''
cmd.retcode
'''
2014-12-23 20:02:56 +00:00
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)
2012-03-27 23:05:38 +00:00
2016-08-30 23:13:12 +00:00
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')
2016-08-30 23:13:12 +00:00
2015-03-02 02:54:22 +00:00
def test_script(self):
'''
cmd.script
'''
args = 'saltines crackers biscuits=yes'
script = 'salt://script.py'
ret = self.run_function('cmd.script', [script, args])
self.assertEqual(ret['stdout'], args)
def test_script_retcode(self):
'''
cmd.script_retcode
'''
script = 'salt://script.py'
ret = self.run_function('cmd.script_retcode', [script])
self.assertEqual(ret, 0)
def test_script_cwd(self):
'''
cmd.script with cwd
'''
tmp_cwd = tempfile.mkdtemp(dir=TMP)
args = 'saltines crackers biscuits=yes'
script = 'salt://script.py'
ret = self.run_function('cmd.script', [script, args], cwd=tmp_cwd)
self.assertEqual(ret['stdout'], args)
def test_script_cwd_with_space(self):
'''
cmd.script with cwd
'''
tmp_cwd = "{0}{1}test 2".format(tempfile.mkdtemp(dir=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)
self.assertEqual(ret['stdout'], args)
2015-03-02 02:54:22 +00:00
@destructiveTest
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)
@skip_if_binaries_missing(['which'])
2012-03-27 23:05:38 +00:00
def test_which(self):
'''
cmd.which
'''
2013-03-21 22:22:29 +00:00
self.assertEqual(self.run_function('cmd.which', ['cat']).rstrip(),
self.run_function('cmd.run', ['which cat']).rstrip())
2012-03-27 23:05:38 +00:00
2015-03-02 02:54:22 +00:00
@skip_if_binaries_missing(['which'])
def test_which_bin(self):
'''
cmd.which_bin
'''
cmds = ['pip3', 'pip2', 'pip', 'pip-python']
2015-03-02 02:54:22 +00:00
ret = self.run_function('cmd.which_bin', [cmds])
self.assertTrue(os.path.split(ret)[1] in cmds)
2012-03-27 23:05:38 +00:00
def test_has_exec(self):
'''
cmd.has_exec
'''
self.assertTrue(self.run_function('cmd.has_exec',
[AVAILABLE_PYTHON_EXECUTABLE]))
2013-03-21 22:22:29 +00:00
self.assertFalse(self.run_function('cmd.has_exec',
['alllfsdfnwieulrrh9123857ygf']))
2012-03-27 23:05:38 +00:00
def test_exec_code(self):
'''
cmd.exec_code
'''
2015-03-02 02:54:22 +00:00
code = textwrap.dedent('''\
import sys
sys.stdout.write('cheese')''')
2013-03-21 22:22:29 +00:00
self.assertEqual(self.run_function('cmd.exec_code',
[AVAILABLE_PYTHON_EXECUTABLE,
code]).rstrip(),
2013-03-21 22:22:29 +00:00
'cheese')
def test_exec_code_with_single_arg(self):
'''
cmd.exec_code
'''
code = textwrap.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)
def test_exec_code_with_multiple_args(self):
'''
cmd.exec_code
'''
code = textwrap.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)
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)
@skip_if_not_root
@skipIf(salt.utils.platform.is_windows, 'skip windows, requires password')
def test_quotes_runas(self):
'''
cmd.run with quoted command
'''
cmd = '''echo 'SELECT * FROM foo WHERE bar="baz"' '''
if salt.utils.platform.is_darwin():
cmd = '''echo 'SELECT * FROM foo WHERE bar=\\"baz\\"' '''
expected_result = 'SELECT * FROM foo WHERE bar="baz"'
2018-05-06 16:54:55 +00:00
runas = this_user()
result = self.run_function('cmd.run_stdout', [cmd],
runas=runas).strip()
self.assertEqual(result, expected_result)
2018-03-30 18:12:02 +00:00
@skipIf(salt.utils.platform.is_windows(), 'minion is windows')
@skip_if_not_root
2018-08-03 19:32:15 +00:00
@destructiveTest
2018-03-30 18:12:02 +00:00
def test_runas(self):
'''
Ensure that the env is the runas user's
'''
2018-08-03 19:32:15 +00:00
if salt.utils.platform.is_darwin():
if self.runas_usr not in self.run_function('user.info', [self.runas_usr]).values():
self.run_function('user.add', [self.runas_usr])
out = self.run_function('cmd.run', ['env'], runas=self.runas_usr).splitlines()
self.assertIn('USER={0}'.format(self.runas_usr), out)
2018-03-30 18:12:02 +00:00
@skipIf(not salt.utils.path.which_bin('sleep'), 'sleep cmd not installed')
def test_timeout(self):
'''
cmd.run trigger timeout
'''
2017-02-26 02:11:06 +00:00
out = self.run_function('cmd.run',
['sleep 2 && echo hello'],
f_timeout=1,
python_shell=True)
self.assertTrue('Timed out' in out)
@skipIf(not salt.utils.path.which_bin('sleep'), 'sleep cmd not installed')
def test_timeout_success(self):
'''
cmd.run sufficient timeout to succeed
'''
2017-02-26 02:11:06 +00:00
out = self.run_function('cmd.run',
['sleep 1 && echo hello'],
f_timeout=2,
python_shell=True)
self.assertEqual(out, 'hello')
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'], '')
2018-05-29 21:51:02 +00:00
def test_cmd_run_whoami(self):
'''
test return of whoami
'''
cmd = self.run_function('cmd.run', ['whoami'])
if salt.utils.platform.is_windows():
2018-05-29 21:51:02 +00:00
self.assertIn('administrator', cmd)
else:
self.assertEqual('root', cmd)