diff --git a/salt/modules/gpg.py b/salt/modules/gpg.py index c353dc2d28..a4396e13a9 100644 --- a/salt/modules/gpg.py +++ b/salt/modules/gpg.py @@ -26,19 +26,6 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs import salt.ext.six as six -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote - -from salt.exceptions import ( - SaltInvocationError -) - -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote # Set up logging log = logging.getLogger(__name__) @@ -92,29 +79,30 @@ except ImportError: pass -def _check_gpg(): +def _gpg(): ''' - Looks to see if gpg binary is present on the system. + Returns the path to the gpg binary ''' # Get the path to the gpg binary. - if salt.utils.which('gpg'): - return __virtualname__ - return (False, 'The gpg execution module cannot be loaded: ' - 'gpg binary is not in the path.') + return salt.utils.which('gpg') def __virtual__(): ''' Makes sure that python-gnupg and gpg are available. ''' - if _check_gpg() and HAS_LIBS: + if not _gpg(): + return (False, 'The gpg execution module cannot be loaded: ' + 'gpg binary is not in the path.') + if HAS_LIBS: gnupg_version = distutils.version.LooseVersion(gnupg.__version__) if gnupg_version >= '1.3.1': global GPG_1_3_1 GPG_1_3_1 = True return __virtualname__ - return (False, 'The gpg execution module cannot be loaded; either the' - ' gnupg module is not installed or the gpg binary is not in the path.') + + return (False, 'The gpg execution module cannot be loaded; the' + ' gnupg python module is not installed.') def _create_gpg(user=None, gnupghome=None): @@ -850,15 +838,18 @@ def trust_key(keyid=None, if trust_level not in _VALID_TRUST_LEVELS: return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS)) - cmd = 'echo "{0}:{1}" | {2} --import-ownertrust'.format(_cmd_quote(fingerprint), - _cmd_quote(NUM_TRUST_DICT[trust_level]), - _cmd_quote(_check_gpg())) + stdin = '{0}:{1}\n'.format(fingerprint, NUM_TRUST_DICT[trust_level]) + cmd = [_gpg(), '--import-ownertrust'] _user = user + if user == 'salt': homeDir = os.path.join(salt.syspaths.CONFIG_DIR, 'gpgkeys') - cmd = '{0} --homedir {1}'.format(cmd, homeDir) + cmd.extend([' --homedir', homeDir]) _user = 'root' - res = __salt__['cmd.run_all'](cmd, runas=_user, python_shell=True) + res = __salt__['cmd.run_all'](cmd, + stdin=stdin, + runas=_user, + python_shell=False) if not res['retcode'] == 0: ret['res'] = False diff --git a/salt/modules/mac_user.py b/salt/modules/mac_user.py index c08441b2ab..297c5487e8 100644 --- a/salt/modules/mac_user.py +++ b/salt/modules/mac_user.py @@ -18,6 +18,7 @@ from salt.ext.six import string_types # Import salt libs import salt.utils +import salt.utils.decorators as decorators from salt.utils.locales import sdecode from salt.exceptions import CommandExecutionError, SaltInvocationError @@ -115,7 +116,8 @@ def add(name, _dscl([name_path, 'RealName', fullname]) # Make sure home directory exists - __salt__['file.mkdir'](name) + if createhome: + __salt__['file.mkdir'](home, user=uid, group=gid) # dscl buffers changes, sleep before setting group membership time.sleep(1) @@ -124,7 +126,7 @@ def add(name, return True -def delete(name, *args): +def delete(name, remove=False, force=False): ''' Remove a user from the minion @@ -134,12 +136,19 @@ def delete(name, *args): salt '*' user.delete foo ''' - ### NOTE: *args isn't used here but needs to be included in this function - ### for compatibility with the user.absent state if salt.utils.contains_whitespace(name): raise SaltInvocationError('Username cannot contain whitespace') if not info(name): return True + + # force is added for compatibility with user.absent state function + if force: + log.warn('force option is unsupported on MacOS, ignoring') + + # remove home directory from filesystem + if remove: + __salt__['file.remove'](info(name)['home']) + # Remove from any groups other than primary group. Needs to be done since # group membership is managed separately from users and an entry for the # user will persist even after the user is removed. @@ -396,6 +405,22 @@ def _format_info(data): 'fullname': data.pw_gecos} +@decorators.which('id') +def primary_group(name): + ''' + Return the primary group of the named user + + .. versionadded:: 2016.3.0 + + CLI Example: + + .. code-block:: bash + + salt '*' user.primary_group saltadmin + ''' + return __salt__['cmd.run'](['id', '-g', '-n', name]) + + def list_groups(name): ''' Return a list of groups the named user belongs to diff --git a/salt/modules/useradd.py b/salt/modules/useradd.py index b9050881cf..3f321c4ca2 100644 --- a/salt/modules/useradd.py +++ b/salt/modules/useradd.py @@ -14,6 +14,7 @@ import copy # Import salt libs import salt.utils +import salt.utils.decorators as decorators from salt.ext import six from salt.exceptions import CommandExecutionError @@ -574,6 +575,22 @@ def _format_info(data): 'homephone': gecos_field[3]} +@decorators.which('id') +def primary_group(name): + ''' + Return the primary group of the named user + + .. versionadded:: 2016.3.0 + + CLI Example: + + .. code-block:: bash + + salt '*' user.primary_group saltadmin + ''' + return __salt__['cmd.run'](['id', '-g', '-n', name]) + + def list_groups(name): ''' Return a list of groups the named user belongs to diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py index f19c2a986d..bd111bea78 100644 --- a/salt/utils/__init__.py +++ b/salt/utils/__init__.py @@ -858,6 +858,41 @@ def path_join(*parts): )) +def abs_readlink(path): + ''' + Return the absolute path. If path is a link, dereference it first. + + .. code-block:: python + + >>> import os + >>> os.makedirs('/tmp/dir') + >>> os.chdir('/tmp/dir') + >>> open('/tmp/dir/target', 'w').close() + >>> os.symlink('target', 'link1') + >>> os.symlink('../dir/target', 'link2') + >>> os.symlink('/tmp/dir/target', 'link3') + >>> abs_readlink('/tmp/dir/target') + '/tmp/dir/target' + >>> abs_readlink('/tmp/dir/link1') + '/tmp/dir/target' + >>> abs_readlink('/tmp/dir/link2') + '/tmp/dir/target' + >>> abs_readlink('/tmp/dir/link3') + '/tmp/dir/target' + >>> from subprocess import call + >>> call(['rm', '-r', '/tmp/dir']) + ''' + if os.path.islink(path): + base, lname = os.path.split(os.path.abspath(path)) + target = os.readlink(path) + if target.startswith(os.sep): + return os.path.abspath(target) + else: # target path is relative to supplied path + return os.path.abspath(os.path.join(base, target)) + else: + return os.path.abspath(path) + + def pem_finger(path=None, key=None, sum_type='sha256'): ''' Pass in either a raw pem string, or the path on disk to the location of a diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 2e728cdc67..a30160e03d 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -78,10 +78,7 @@ if salt.utils.is_windows(): import win32api -if platform.uname()[0] == 'Darwin': - SYS_TMP_DIR = '/tmp' -else: - SYS_TMP_DIR = os.environ.get('TMPDIR', tempfile.gettempdir()) +SYS_TMP_DIR = salt.utils.abs_readlink(os.environ.get('TMPDIR', tempfile.gettempdir())) # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') diff --git a/tests/integration/files/file/base/_modules/runtests_helpers.py b/tests/integration/files/file/base/_modules/runtests_helpers.py index 5438d51f24..d6885d5726 100644 --- a/tests/integration/files/file/base/_modules/runtests_helpers.py +++ b/tests/integration/files/file/base/_modules/runtests_helpers.py @@ -12,7 +12,11 @@ from __future__ import absolute_import import os import tempfile -SYS_TMP_DIR = tempfile.gettempdir() +# Import salt libs +import salt.utils + + +SYS_TMP_DIR = salt.utils.abs_readlink(os.environ.get('TMPDIR', tempfile.gettempdir())) # This tempdir path is defined on tests.integration.__init__ TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') diff --git a/tests/integration/modules/disk.py b/tests/integration/modules/disk.py index 449d055a7d..299ed4b6a1 100644 --- a/tests/integration/modules/disk.py +++ b/tests/integration/modules/disk.py @@ -18,27 +18,24 @@ import salt.utils import salt.ext.six as six +@destructiveTest +@skipIf(salt.utils.is_windows(), 'No mtab on Windows') +@skipIf(salt.utils.is_darwin(), 'No mtab on Darwin') class DiskModuleVirtualizationTest(integration.ModuleCase): ''' Test to make sure we return a clean result under Docker. Refs #8976 This is factored into its own class so that we can have some certainty that setUp() and tearDown() are run. ''' - @destructiveTest - @skipIf(salt.utils.is_windows(), 'No mtab on Windows') def setUp(self): # Make /etc/mtab unreadable if os.path.isfile('/etc/mtab'): shutil.move('/etc/mtab', '/tmp/mtab') - @destructiveTest - @skipIf(salt.utils.is_windows(), 'No mtab on Windows') def test_no_mtab(self): ret = self.run_function('disk.usage') self.assertDictEqual(ret, {}) - @destructiveTest - @skipIf(salt.utils.is_windows(), 'No mtab on Windows') def tearDown(self): if os.path.isfile('/tmp/mtab'): shutil.move('/tmp/mtab', '/etc/mtab') @@ -56,12 +53,23 @@ class DiskModuleTest(integration.ModuleCase): self.assertTrue(isinstance(ret, dict)) if not isinstance(ret, dict): return - for key, val in six.iteritems(ret): - self.assertTrue('filesystem' in val) - self.assertTrue('1K-blocks' in val) - self.assertTrue('used' in val) - self.assertTrue('available' in val) - self.assertTrue('capacity' in val) + if salt.utils.is_darwin(): + for key, val in six.iteritems(ret): + self.assertTrue('filesystem' in val) + self.assertTrue('512-blocks' in val) + self.assertTrue('used' in val) + self.assertTrue('available' in val) + self.assertTrue('capacity' in val) + self.assertTrue('iused' in val) + self.assertTrue('ifree' in val) + self.assertTrue('%iused' in val) + else: + for key, val in six.iteritems(ret): + self.assertTrue('filesystem' in val) + self.assertTrue('1K-blocks' in val) + self.assertTrue('used' in val) + self.assertTrue('available' in val) + self.assertTrue('capacity' in val) def test_inodeusage(self): ''' @@ -81,4 +89,4 @@ class DiskModuleTest(integration.ModuleCase): if __name__ == '__main__': from integration import run_tests - run_tests(DiskModuleTest) + run_tests([DiskModuleVirtualizationTest, DiskModuleTest]) diff --git a/tests/integration/modules/mac_user.py b/tests/integration/modules/mac_user.py index f64c9edfc5..d7e512237d 100644 --- a/tests/integration/modules/mac_user.py +++ b/tests/integration/modules/mac_user.py @@ -38,9 +38,13 @@ def __random_string(size=6): # Create user strings for tests ADD_USER = __random_string() DEL_USER = __random_string() +PRIMARY_GROUP_USER = __random_string() CHANGE_USER = __random_string() +@destructiveTest +@skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test') +@requires_system_grains class MacUserModuleTest(integration.ModuleCase): ''' Integration tests for the mac_user module @@ -59,9 +63,6 @@ class MacUserModuleTest(integration.ModuleCase): ) ) - @destructiveTest - @skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test') - @requires_system_grains def test_mac_user_add(self, grains=None): ''' Tests the add function @@ -74,9 +75,6 @@ class MacUserModuleTest(integration.ModuleCase): self.run_function('user.delete', [ADD_USER]) raise - @destructiveTest - @skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test') - @requires_system_grains def test_mac_user_delete(self, grains=None): ''' Tests the delete function @@ -94,9 +92,26 @@ class MacUserModuleTest(integration.ModuleCase): except CommandExecutionError: raise - @destructiveTest - @skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test') - @requires_system_grains + def test_mac_user_primary_group(self, grains=None): + ''' + Tests the primary_group function + ''' + + # Create a user to test primary group function + if self.run_function('user.add', [PRIMARY_GROUP_USER]) is not True: + self.run_function('user.delete', [PRIMARY_GROUP_USER]) + self.skipTest('Failed to create a user') + + try: + # Test mac_user.primary_group + primary_group = self.run_function('user.primary_group', [PRIMARY_GROUP_USER]) + uid_info = self.run_function('user.info', [PRIMARY_GROUP_USER]) + self.assertIn(primary_group, uid_info['groups']) + + except AssertionError: + self.run_function('user.delete', [PRIMARY_GROUP_USER]) + raise + def test_mac_user_changes(self, grains=None): ''' Tests mac_user functions that change user properties @@ -107,7 +122,7 @@ class MacUserModuleTest(integration.ModuleCase): self.skipTest('Failed to create a user') try: - # Test mac_user.chudi + # Test mac_user.chuid self.run_function('user.chuid', [CHANGE_USER, 4376]) uid_info = self.run_function('user.info', [CHANGE_USER]) self.assertEqual(uid_info['uid'], 4376) @@ -141,9 +156,6 @@ class MacUserModuleTest(integration.ModuleCase): self.run_function('user.delete', [CHANGE_USER]) raise - @destructiveTest - @skipIf(os.geteuid() != 0, 'You must be logged in as root to run this test') - @requires_system_grains def tearDown(self, grains=None): ''' Clean up after tests diff --git a/tests/integration/modules/useradd.py b/tests/integration/modules/useradd.py index 335a02c7b7..cf5fa23202 100644 --- a/tests/integration/modules/useradd.py +++ b/tests/integration/modules/useradd.py @@ -16,12 +16,18 @@ from salttesting.helpers import ( ensure_in_syspath('../../') # Import salt libs +import salt.utils import integration # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +@destructiveTest +@skipIf(os.geteuid() != 0, 'you must be root to run these tests') +# Only run on linux for now until or if we can figure out a way to use +# __grains__ inside of useradd.__virtual__ +@skipIf(not salt.utils.is_linux(), 'These tests can only be run on linux') class UseraddModuleTest(integration.ModuleCase): def setUp(self): @@ -40,8 +46,6 @@ class UseraddModuleTest(integration.ModuleCase): for x in range(size) ) - @destructiveTest - @skipIf(os.geteuid() != 0, 'you must be root to run this test') @requires_system_grains def test_groups_includes_primary(self, grains=None): # Let's create a user, which usually creates the group matching the @@ -85,6 +89,27 @@ class UseraddModuleTest(integration.ModuleCase): self.run_function('user.delete', [uname, True, True]) raise + def test_linux_user_primary_group(self, grains=None): + ''' + Tests the primary_group function + ''' + name = 'saltyuser' + + # Create a user to test primary group function + if self.run_function('user.add', [name]) is not True: + self.run_function('user.delete', [name]) + self.skipTest('Failed to create a user') + + try: + # Test useradd.primary_group + primary_group = self.run_function('user.primary_group', [name]) + uid_info = self.run_function('user.info', [name]) + self.assertIn(primary_group, uid_info['groups']) + + except: + self.run_function('user.delete', [name]) + raise + if __name__ == '__main__': from integration import run_tests diff --git a/tests/integration/shell/enabled.py b/tests/integration/shell/enabled.py index 185738157c..9e80a63d5e 100644 --- a/tests/integration/shell/enabled.py +++ b/tests/integration/shell/enabled.py @@ -31,7 +31,7 @@ class EnabledTest(integration.ModuleCase): ''' ensure that python_shell defaults to True for cmd.run ''' - enabled_ret = '3\nsaltines' + enabled_ret = '3\nsaltines' # the result of running self.cmd in a shell ret = self.run_function('cmd.run', [self.cmd]) self.assertEqual(ret, enabled_ret) @@ -52,12 +52,12 @@ class EnabledTest(integration.ModuleCase): state_filename = state_name + '.sls' state_file = os.path.join(STATE_DIR, state_filename) - enabled_ret = '3 saltines' + enabled_ret = '3 saltines' # the result of running self.cmd in a shell ret_key = 'test_|-shell_enabled_|-{0}_|-configurable_test_state'.format(enabled_ret) try: salt.utils.fopen(state_file, 'w').write(textwrap.dedent('''\ - {{% set shell_enabled = salt['cmd.run']("{0}") %}} + {{% set shell_enabled = salt['cmd.run']("{0}").strip() %}} shell_enabled: test.configurable_test_state: @@ -77,6 +77,7 @@ class EnabledTest(integration.ModuleCase): state_filename = state_name + '.sls' state_file = os.path.join(STATE_DIR, state_filename) + # the result of running self.cmd not in a shell disabled_ret = ('first second third | wc -l ; export SALTY_VARIABLE=saltines ' '&& echo $SALTY_VARIABLE ; echo duh &> /dev/null') ret_key = 'test_|-shell_enabled_|-{0}_|-configurable_test_state'.format(disabled_ret) diff --git a/tests/integration/states/cmd.py b/tests/integration/states/cmd.py index 9657a707df..508dce5004 100644 --- a/tests/integration/states/cmd.py +++ b/tests/integration/states/cmd.py @@ -142,14 +142,14 @@ class CMDTest(integration.ModuleCase, state_filename = state_name + '.sls' state_file = os.path.join(STATE_DIR, state_filename) - saltines_key = 'cmd_|-saltines_|-/bin/true_|-run' + saltines_key = 'cmd_|-saltines_|-echo_|-run' biscuits_key = 'cmd_|-biscuits_|-echo hello_|-wait' try: salt.utils.fopen(state_file, 'w').write(textwrap.dedent('''\ saltines: cmd.run: - - name: /bin/true + - name: echo - cwd: / - stateful: True diff --git a/tests/integration/states/file.py b/tests/integration/states/file.py index cd956b522c..a005a67f0e 100644 --- a/tests/integration/states/file.py +++ b/tests/integration/states/file.py @@ -1831,7 +1831,9 @@ class FileTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn): self.assertEqual(pwd.getpwuid(onestats.st_uid).pw_name, user) self.assertEqual(pwd.getpwuid(twostats.st_uid).pw_name, 'root') self.assertEqual(grp.getgrgid(onestats.st_gid).gr_name, group) - self.assertEqual(grp.getgrgid(twostats.st_gid).gr_name, 'root') + if salt.utils.which('id'): + root_group = self.run_function('user.primary_group', ['root']) + self.assertEqual(grp.getgrgid(twostats.st_gid).gr_name, root_group) finally: if os.path.isdir(tmp_dir): shutil.rmtree(tmp_dir) diff --git a/tests/integration/states/pkg.py b/tests/integration/states/pkg.py index a4e4fa01db..a8481d561d 100644 --- a/tests/integration/states/pkg.py +++ b/tests/integration/states/pkg.py @@ -30,7 +30,8 @@ _PKG_TARGETS = { 'Debian': ['python-plist', 'apg'], 'RedHat': ['xz-devel', 'zsh-html'], 'FreeBSD': ['aalib', 'pth'], - 'Suse': ['aalib', 'python-pssh'] + 'Suse': ['aalib', 'python-pssh'], + 'MacOS': ['libpng', 'jpeg'], } _PKG_TARGETS_32 = { diff --git a/tests/integration/states/ssh.py b/tests/integration/states/ssh.py index 7cda2bb26b..40359f3fe8 100644 --- a/tests/integration/states/ssh.py +++ b/tests/integration/states/ssh.py @@ -246,4 +246,4 @@ class SSHAuthStateTests(integration.ModuleCase, if __name__ == '__main__': from integration import run_tests - run_tests(SSHKnownHostsStateTest) + run_tests([SSHKnownHostsStateTest, SSHAuthStateTests]) diff --git a/tests/integration/states/user.py b/tests/integration/states/user.py index 0f905f3e32..5ff893ce5a 100644 --- a/tests/integration/states/user.py +++ b/tests/integration/states/user.py @@ -22,6 +22,7 @@ from salttesting.helpers import ( ensure_in_syspath('../../') # Import salt libs +import salt.utils import integration @@ -107,8 +108,12 @@ class UserTest(integration.ModuleCase, If you run the test and it fails, please fix the code it's testing to work on your operating system. ''' + # MacOS users' primary group defaults to staff (20), not the name of + # user + gid_from_name = False if grains['os_family'] == 'MacOS' else True + ret = self.run_state('user.present', name='salt_test', - gid_from_name=True, home='/var/lib/salt_test') + gid_from_name=gid_from_name, home='/var/lib/salt_test') self.assertSaltTrueReturn(ret) ret = self.run_function('user.info', ['salt_test']) @@ -118,6 +123,8 @@ class UserTest(integration.ModuleCase, self.assertTrue(os.path.isdir('/var/lib/salt_test')) if grains['os_family'] in ('Suse',): self.assertEqual(group_name, 'users') + elif grains['os_family'] == 'MacOS': + self.assertEqual(group_name, 'staff') else: self.assertEqual(group_name, 'salt_test') @@ -186,9 +193,11 @@ class UserTest(integration.ModuleCase, ret = self.run_function('user.info', ['salt_test']) self.assertReturnNonEmptySaltType(ret) self.assertEqual('', ret['fullname']) - self.assertEqual('', ret['roomnumber']) - self.assertEqual('', ret['workphone']) - self.assertEqual('', ret['homephone']) + # MacOS does not supply the following GECOS fields + if not salt.utils.is_darwin(): + self.assertEqual('', ret['roomnumber']) + self.assertEqual('', ret['workphone']) + self.assertEqual('', ret['homephone']) ret = self.run_state('user.absent', name='salt_test') self.assertSaltTrueReturn(ret) diff --git a/tests/runtests.py b/tests/runtests.py index f571599562..17f0f2bd7a 100644 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -40,7 +40,17 @@ try: except OSError as err: print('Failed to change directory to salt\'s source: {0}'.format(err)) -REQUIRED_OPEN_FILES = 3072 +# Soft and hard limits on max open filehandles +MAX_OPEN_FILES = { + 'integration': { + 'soft_limit': 3072, + 'hard_limit': 4096, + }, + 'unit': { + 'soft_limit': 1024, + 'hard_limit': 2048, + }, +} class SaltTestsuiteParser(SaltCoverageTestingParser): @@ -292,7 +302,7 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): def start_daemons_only(self): if not salt.utils.is_windows(): - self.prep_filehandles() + self.set_filehandle_limits('integration') try: print_header( ' * Setting up Salt daemons for interactive use', @@ -335,32 +345,51 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): while True: time.sleep(1) - def prep_filehandles(self): - smax_open_files, hmax_open_files = resource.getrlimit( - resource.RLIMIT_NOFILE - ) - if smax_open_files < REQUIRED_OPEN_FILES: + def set_filehandle_limits(self, limits='integration'): + ''' + Set soft and hard limits on open file handles at required thresholds + for integration tests or unit tests + ''' + # Get current limits + prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE) + + # Get required limits + min_soft = MAX_OPEN_FILES[limits]['soft_limit'] + min_hard = MAX_OPEN_FILES[limits]['hard_limit'] + + # Check minimum required limits + set_limits = False + if prev_soft < min_soft: + soft = min_soft + set_limits = True + else: + soft = prev_soft + + if prev_hard < min_hard: + hard = min_hard + set_limits = True + else: + hard = prev_hard + + # Increase limits + if set_limits: print( - ' * Max open files setting is too low({0}) for running the ' - 'tests'.format(smax_open_files) + ' * Max open files settings is too low (soft: {0}, hard: {1}) ' + 'for running the tests'.format(prev_soft, prev_hard) ) print( - ' * Trying to raise the limit to {0}'.format(REQUIRED_OPEN_FILES) + ' * Trying to raise the limits to soft: ' + '{0}, hard: {1}'.format(soft, hard) ) - if hmax_open_files < 4096: - hmax_open_files = 4096 # Decent default? try: - resource.setrlimit( - resource.RLIMIT_NOFILE, - (REQUIRED_OPEN_FILES, hmax_open_files) - ) + resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard)) except Exception as err: print( - 'ERROR: Failed to raise the max open files setting -> ' + 'ERROR: Failed to raise the max open files settings -> ' '{0}'.format(err) ) print('Please issue the following command on your console:') - print(' ulimit -n {0}'.format(REQUIRED_OPEN_FILES)) + print(' ulimit -n {0}'.format(soft)) self.exit() finally: print('~' * getattr(self.options, 'output_columns', PNUM)) @@ -401,7 +430,7 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): # We don't need the tests daemon running return [True] if not salt.utils.is_windows(): - self.prep_filehandles() + self.set_filehandle_limits('integration') try: print_header( @@ -480,6 +509,9 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): status = [] if self.options.unit: + # MacOS needs more open filehandles for running unit test suite + self.set_filehandle_limits('unit') + results = self.run_suite( os.path.join(TEST_DIR, 'unit'), 'Unit', '*_test.py' ) diff --git a/tests/unit/modules/blockdev_test.py b/tests/unit/modules/blockdev_test.py index 77b574e149..6009a101e9 100644 --- a/tests/unit/modules/blockdev_test.py +++ b/tests/unit/modules/blockdev_test.py @@ -37,6 +37,8 @@ class TestBlockdevModule(TestCase): ret = blockdev.tune('/dev/sda', **kwargs) self.assertTrue(ret) + @skipIf(not salt.utils.which('sync'), 'sync not found') + @skipIf(not salt.utils.which('mkfs'), 'mkfs not found') def test_format(self): ''' unit tests for blockdev.format diff --git a/tests/unit/modules/btrfs_test.py b/tests/unit/modules/btrfs_test.py index 28aa7b34dd..40d116095d 100644 --- a/tests/unit/modules/btrfs_test.py +++ b/tests/unit/modules/btrfs_test.py @@ -7,6 +7,7 @@ from __future__ import absolute_import # Import Salt Testing Libs from salttesting import TestCase, skipIf from salttesting.mock import ( + mock_open, MagicMock, patch, NO_MOCK, @@ -87,23 +88,27 @@ class BtrfsTestCase(TestCase): ret = [{'range': '/dev/sda1', 'mount_point': False, 'log': False, 'passed': True}] - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertListEqual(btrfs.defragment('/dev/sda1'), ret) + mock_run = MagicMock(return_value={'retcode': 1, + 'stderr': '', + 'stdout': 'Salt'}) + with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): + mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') + with patch.object(salt.utils, 'fopen', mock_file): + self.assertListEqual(btrfs.defragment('/dev/sda1'), ret) @patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)) def test_defragment_error(self): ''' Test if it gives device not mount error ''' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, btrfs.defragment, - '/dev/sda1') + mock_run = MagicMock(return_value={'retcode': 1, + 'stderr': '', + 'stdout': 'Salt'}) + with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): + mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') + with patch.object(salt.utils, 'fopen', mock_file): + self.assertRaises(CommandExecutionError, btrfs.defragment, + '/dev/sda1') # 'features' function tests: 1 @@ -158,13 +163,15 @@ class BtrfsTestCase(TestCase): ''' Test if it create a file system on the specified device. ''' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) + mock_cmd = MagicMock(return_value={'retcode': 1, + 'stderr': '', + 'stdout': 'Salt'}) mock_info = MagicMock(return_value=[]) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock, + with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_cmd, 'btrfs.info': mock_info}): - self.assertDictEqual(btrfs.mkfs('/dev/sda1'), {'log': 'Salt'}) + mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') + with patch.object(salt.utils, 'fopen', mock_file): + self.assertDictEqual(btrfs.mkfs('/dev/sda1'), {'log': 'Salt'}) def test_mkfs_error(self): ''' @@ -304,14 +311,16 @@ class BtrfsTestCase(TestCase): ''' Test if it gives migration error ''' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': {'type': 'ext4'}}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock): - self.assertRaises(CommandExecutionError, btrfs.convert, - '/dev/sda1') + mock_run = MagicMock(return_value={'retcode': 1, + 'stderr': '', + 'stdout': 'Salt'}) + with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): + mock_blk = MagicMock(return_value={'/dev/sda1': {'type': 'ext4'}}) + with patch.object(salt.utils.fsutils, '_blkid_output', mock_blk): + mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') + with patch.object(salt.utils, 'fopen', mock_file): + self.assertRaises(CommandExecutionError, btrfs.convert, + '/dev/sda1') # 'add' function tests: 1 diff --git a/tests/unit/modules/linux_sysctl_test.py b/tests/unit/modules/linux_sysctl_test.py index ca93749a33..89bea83e5b 100644 --- a/tests/unit/modules/linux_sysctl_test.py +++ b/tests/unit/modules/linux_sysctl_test.py @@ -97,6 +97,7 @@ class LinuxSysctlTestCase(TestCase): 1, config=None) @patch('os.path.isfile', MagicMock(return_value=False)) + @patch('os.path.exists', MagicMock(return_value=True)) def test_persist_no_conf_success(self): ''' Tests successful add of config file when previously not one @@ -120,6 +121,7 @@ class LinuxSysctlTestCase(TestCase): '#\n# Kernel sysctl configuration\n#\n') @patch('os.path.isfile', MagicMock(return_value=True)) + @patch('os.path.exists', MagicMock(return_value=True)) def test_persist_read_conf_success(self): ''' Tests sysctl.conf read success diff --git a/tests/unit/modules/mount_test.py b/tests/unit/modules/mount_test.py index c304531145..9c4ad6f363 100644 --- a/tests/unit/modules/mount_test.py +++ b/tests/unit/modules/mount_test.py @@ -106,12 +106,13 @@ class MountTestCase(TestCase): ''' Remove the mount point from the fstab ''' - mock = MagicMock(return_value={}) - with patch.object(mount, 'fstab', mock): - self.assertTrue(mount.rm_fstab('name', 'device')) + mock_fstab = MagicMock(return_value={}) + with patch.object(mount, 'fstab', mock_fstab): + with patch('salt.utils.fopen', mock_open()): + self.assertTrue(mount.rm_fstab('name', 'device')) - mock = MagicMock(return_value={'name': 'name'}) - with patch.object(mount, 'fstab', mock): + mock_fstab = MagicMock(return_value={'name': 'name'}) + with patch.object(mount, 'fstab', mock_fstab): with patch('salt.utils.fopen', mock_open()) as m_open: helper_open = m_open() helper_open.write.assertRaises(CommandExecutionError, diff --git a/tests/unit/modules/ps_test.py b/tests/unit/modules/ps_test.py index 063d724acf..a0accb1161 100644 --- a/tests/unit/modules/ps_test.py +++ b/tests/unit/modules/ps_test.py @@ -13,9 +13,15 @@ from salttesting.mock import MagicMock, patch, call, Mock ensure_in_syspath('../../') +# Import Salt libs from salt.modules import ps +import salt.ext.six as six -HAS_PSUTIL = ps.__virtual__() +ps_virtual = ps.__virtual__() +if ps_virtual is True or isinstance(ps_virtual, six.string_types): + HAS_PSUTIL = True +else: + HAS_PSUTIL = False HAS_PSUTIL_VERSION = False # Import 3rd-party libs diff --git a/tests/unit/modules/pw_group_test.py b/tests/unit/modules/pw_group_test.py index 7b6d87c4ed..696111f417 100644 --- a/tests/unit/modules/pw_group_test.py +++ b/tests/unit/modules/pw_group_test.py @@ -63,8 +63,10 @@ class PwGroupTestCase(TestCase): ''' Tests for return info on all groups ''' - mock = MagicMock(return_value={'group.getent': 1}) - with patch.dict(pw_group.__context__, mock): + mock_getent = [{'passwd': 'x', + 'gid': 0, + 'name': 'root'}] + with patch.dict(pw_group.__context__, {'group.getent': mock_getent}): self.assertDictContainsSubset({'passwd': 'x', 'gid': 0, 'name': 'root'}, pw_group.getent()[0]) diff --git a/tests/unit/renderers/gpg_test.py b/tests/unit/renderers/gpg_test.py index c8adff5a0f..76245ac6ff 100644 --- a/tests/unit/renderers/gpg_test.py +++ b/tests/unit/renderers/gpg_test.py @@ -51,6 +51,7 @@ class GPGTestCase(TestCase): with patch.dict(gpg.__salt__, {'config.get': MagicMock(return_value=False)}): self.assertEqual(gpg._get_key_dir(), def_dir) + @patch('salt.utils.which', MagicMock()) def test__decrypt_ciphertext(self): ''' test _decrypt_ciphertext diff --git a/tests/unit/states/file_test.py b/tests/unit/states/file_test.py index 2508fd2238..e9e664072b 100644 --- a/tests/unit/states/file_test.py +++ b/tests/unit/states/file_test.py @@ -7,7 +7,7 @@ import pprint # Import Salt Testing libs from salttesting import skipIf, TestCase -from salttesting.helpers import ensure_in_syspath +from salttesting.helpers import destructiveTest, ensure_in_syspath from salttesting.mock import ( NO_MOCK, NO_MOCK_REASON, @@ -959,11 +959,12 @@ class FileTestCase(TestCase): # 'comment' function tests: 1 + @destructiveTest def test_comment(self): ''' Test to comment out specified lines in a file. ''' - name = '/etc/fstab' + name = '/etc/aliases' if salt.utils.is_darwin() else '/etc/fstab' regex = 'bind 127.0.0.1' ret = {'name': name, @@ -1003,7 +1004,7 @@ class FileTestCase(TestCase): 'file.comment_line': mock_t}): with patch.dict(filestate.__opts__, {'test': True}): comt = ('File {0} is set to be updated'.format(name)) - ret.update({'comment': comt, 'result': None, 'pchanges': {'/etc/fstab': 'updated'}}) + ret.update({'comment': comt, 'result': None, 'pchanges': {name: 'updated'}}) self.assertDictEqual(filestate.comment(name, regex), ret) with patch.dict(filestate.__opts__, {'test': False}): @@ -1016,11 +1017,12 @@ class FileTestCase(TestCase): # 'uncomment' function tests: 1 + @destructiveTest def test_uncomment(self): ''' Test to uncomment specified commented lines in a file ''' - name = '/etc/fstab' + name = '/etc/aliases' if salt.utils.is_darwin() else '/etc/fstab' regex = 'bind 127.0.0.1' ret = {'name': name, @@ -1058,7 +1060,7 @@ class FileTestCase(TestCase): with patch.dict(filestate.__opts__, {'test': True}): comt = ('File {0} is set to be updated'.format(name)) - ret.update({'comment': comt, 'result': None, 'pchanges': {'/etc/fstab': 'updated'}, }) + ret.update({'comment': comt, 'result': None, 'pchanges': {name: 'updated'}, }) self.assertDictEqual(filestate.uncomment(name, regex), ret) with patch.dict(filestate.__opts__, {'test': False}): diff --git a/tests/unit/transport/tcp_test.py b/tests/unit/transport/tcp_test.py index 0d4a961afc..8e6f45e707 100644 --- a/tests/unit/transport/tcp_test.py +++ b/tests/unit/transport/tcp_test.py @@ -74,6 +74,7 @@ class BaseTCPReqCase(TestCase): del cls.server_channel +@skipIf(salt.utils.is_darwin(), 'hanging test suite on MacOS') class ClearReqTestCases(BaseTCPReqCase, ReqChannelMixin): ''' Test all of the clear msg stuff @@ -90,6 +91,7 @@ class ClearReqTestCases(BaseTCPReqCase, ReqChannelMixin): raise tornado.gen.Return((payload, {'fun': 'send_clear'})) +@skipIf(salt.utils.is_darwin(), 'hanging test suite on MacOS') class AESReqTestCases(BaseTCPReqCase, ReqChannelMixin): def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_opts)