mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 08:35:21 +00:00
Merge branch 'merge-forward/2018.3-to-2019.2' into merge-forward/2019.2-develop
Conflicts: * Gemfile * salt/modules/file.py * salt/modules/win_task.py * tests/unit/modules/test_file.py
This commit is contained in:
commit
dcd86a1894
1
Gemfile
1
Gemfile
@ -30,4 +30,5 @@ group :macos do
|
||||
gem 'rbnacl', '< 5.0', :require => false
|
||||
gem 'rbnacl-libsodium', :require => false
|
||||
gem 'bcrypt_pbkdf', '< 2.0', :require => false
|
||||
gem 'ffi', '= 1.10.0', :require => false
|
||||
end
|
||||
|
@ -30,5 +30,6 @@ This section contains a list of the Python modules that are used to extend the v
|
||||
../ref/serializers/all/index
|
||||
../ref/states/all/index
|
||||
../ref/thorium/all/index
|
||||
../ref/tokens/all/index
|
||||
../ref/tops/all/index
|
||||
../ref/wheel/all/index
|
||||
|
14
doc/ref/tokens/all/index.rst
Normal file
14
doc/ref/tokens/all/index.rst
Normal file
@ -0,0 +1,14 @@
|
||||
.. _all-salt.tokens:
|
||||
|
||||
============
|
||||
auth modules
|
||||
============
|
||||
|
||||
.. currentmodule:: salt.tokens
|
||||
|
||||
.. autosummary::
|
||||
:toctree:
|
||||
:template: autosummary.rst.tmpl
|
||||
|
||||
localfs
|
||||
rediscluster
|
6
doc/ref/tokens/all/salt.tokens.localfs.rst
Normal file
6
doc/ref/tokens/all/salt.tokens.localfs.rst
Normal file
@ -0,0 +1,6 @@
|
||||
===================
|
||||
salt.tokens.localfs
|
||||
===================
|
||||
|
||||
.. automodule:: salt.tokens.localfs
|
||||
:members:
|
6
doc/ref/tokens/all/salt.tokens.rediscluster.rst
Normal file
6
doc/ref/tokens/all/salt.tokens.rediscluster.rst
Normal file
@ -0,0 +1,6 @@
|
||||
========================
|
||||
salt.tokens.rediscluster
|
||||
========================
|
||||
|
||||
.. automodule:: salt.tokens.rediscluster
|
||||
:members:
|
@ -61,6 +61,7 @@ import datetime
|
||||
|
||||
try:
|
||||
from M2Crypto import EVP
|
||||
HAS_REQUIRED_CRYPTO = True
|
||||
HAS_M2 = True
|
||||
except ImportError:
|
||||
HAS_M2 = False
|
||||
|
@ -22,7 +22,6 @@ from salt.ext.six.moves import queue
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
# Since the NullHandler is only available on python >= 2.7, here's a copy
|
||||
# with NewStyleClassMixIn so it's also a new style class
|
||||
|
@ -63,6 +63,7 @@ def _clear_context():
|
||||
def _yes():
|
||||
'''
|
||||
Returns ['--yes'] if on v0.9.9.0 or later, otherwise returns an empty list
|
||||
Confirm all prompts (--yes_ is available on v0.9.9.0 or later
|
||||
'''
|
||||
if 'chocolatey._yes' in __context__:
|
||||
return __context__['chocolatey._yes']
|
||||
@ -87,7 +88,7 @@ def _no_progress():
|
||||
log.warning('--no-progress unsupported in choco < 0.10.4')
|
||||
answer = []
|
||||
__context__['chocolatey._no_progress'] = answer
|
||||
return __context__['chocolatey._no_progress']
|
||||
return answer
|
||||
|
||||
|
||||
def _find_chocolatey():
|
||||
|
@ -30,7 +30,7 @@ import time
|
||||
import glob
|
||||
import hashlib
|
||||
import mmap
|
||||
from collections import Iterable, Mapping
|
||||
from collections import Iterable, Mapping, namedtuple
|
||||
from functools import reduce # pylint: disable=redefined-builtin
|
||||
|
||||
# pylint: disable=import-error,no-name-in-module,redefined-builtin
|
||||
@ -73,6 +73,9 @@ __func_alias__ = {
|
||||
}
|
||||
|
||||
|
||||
AttrChanges = namedtuple('AttrChanges', 'added,removed')
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on POSIX-like systems
|
||||
@ -161,33 +164,49 @@ def _splitlines_preserving_trailing_newline(str):
|
||||
return lines
|
||||
|
||||
|
||||
def _get_chattr_man():
|
||||
'''
|
||||
Get the contents of the chattr man page
|
||||
'''
|
||||
return subprocess.check_output(['man', 'chattr'])
|
||||
|
||||
|
||||
def _parse_chattr_man(man):
|
||||
'''
|
||||
Parse the contents of a chattr man page to find the E2fsprogs version
|
||||
'''
|
||||
match = re.search(
|
||||
r'E2fsprogs version [0-9\.]+',
|
||||
salt.utils.stringutils.to_str(man),
|
||||
)
|
||||
if match:
|
||||
version = match.group().strip('E2fsprogs version ')
|
||||
else:
|
||||
version = None
|
||||
return version
|
||||
|
||||
|
||||
def _chattr_version():
|
||||
'''
|
||||
Return the version of chattr installed
|
||||
'''
|
||||
return _parse_chattr_man(_get_chattr_man())
|
||||
# There's no really *good* way to get the version of chattr installed.
|
||||
# It's part of the e2fsprogs package - we could try to parse the version
|
||||
# from the package manager, but there's no guarantee that it was
|
||||
# installed that way.
|
||||
#
|
||||
# The most reliable approach is to just check tune2fs, since that should
|
||||
# be installed with chattr, at least if it was installed in a conventional
|
||||
# manner.
|
||||
#
|
||||
# See https://unix.stackexchange.com/a/520399/5788 for discussion.
|
||||
tune2fs = salt.utils.path.which('tune2fs')
|
||||
if not tune2fs or salt.utils.platform.is_aix():
|
||||
return None
|
||||
cmd = [tune2fs]
|
||||
result = __salt__['cmd.run'](cmd, ignore_retcode=True, python_shell=False)
|
||||
match = re.search(
|
||||
r'tune2fs (?P<version>[0-9\.]+)',
|
||||
salt.utils.stringutils.to_str(result),
|
||||
)
|
||||
if match is None:
|
||||
version = None
|
||||
else:
|
||||
version = match.group('version')
|
||||
|
||||
return version
|
||||
|
||||
|
||||
def _chattr_has_extended_attrs():
|
||||
'''
|
||||
Return ``True`` if chattr supports extended attributes, that is,
|
||||
the version is >1.41.22. Otherwise, ``False``
|
||||
'''
|
||||
ver = _chattr_version()
|
||||
if ver is None:
|
||||
return False
|
||||
|
||||
needed_version = salt.utils.versions.LooseVersion('1.41.12')
|
||||
chattr_version = salt.utils.versions.LooseVersion(ver)
|
||||
return chattr_version > needed_version
|
||||
|
||||
|
||||
def gid_to_group(gid):
|
||||
@ -551,8 +570,6 @@ def _cmp_attrs(path, attrs):
|
||||
attrs
|
||||
string of attributes to compare against a given file
|
||||
'''
|
||||
diff = [None, None]
|
||||
|
||||
# lsattr for AIX is not the same thing as lsattr for linux.
|
||||
if salt.utils.platform.is_aix():
|
||||
return None
|
||||
@ -563,15 +580,13 @@ def _cmp_attrs(path, attrs):
|
||||
# lsattr not installed
|
||||
return None
|
||||
|
||||
old = [chr for chr in lattrs if chr not in attrs]
|
||||
if old:
|
||||
diff[1] = ''.join(old)
|
||||
new = set(attrs)
|
||||
old = set(lattrs)
|
||||
|
||||
new = [chr for chr in attrs if chr not in lattrs]
|
||||
if new:
|
||||
diff[0] = ''.join(new)
|
||||
|
||||
return diff
|
||||
return AttrChanges(
|
||||
added=''.join(new-old) or None,
|
||||
removed=''.join(old-new) or None,
|
||||
)
|
||||
|
||||
|
||||
def lsattr(path):
|
||||
@ -607,15 +622,12 @@ def lsattr(path):
|
||||
results = {}
|
||||
for line in result.splitlines():
|
||||
if not line.startswith('lsattr: '):
|
||||
vals = line.split(None, 1)
|
||||
needed_version = salt.utils.versions.LooseVersion('1.41.12')
|
||||
chattr_version = salt.utils.versions.LooseVersion(_chattr_version())
|
||||
# The version of chattr on Centos 6 does not support extended
|
||||
# attributes.
|
||||
if chattr_version > needed_version:
|
||||
results[vals[1]] = re.findall(r"[aAcCdDeijPsStTu]", vals[0])
|
||||
attrs, file = line.split(None, 1)
|
||||
if _chattr_has_extended_attrs():
|
||||
pattern = r"[aAcCdDeijPsStTu]"
|
||||
else:
|
||||
results[vals[1]] = re.findall(r"[acdijstuADST]", vals[0])
|
||||
pattern = r"[acdijstuADST]"
|
||||
results[file] = re.findall(pattern, attrs)
|
||||
|
||||
return results
|
||||
|
||||
@ -680,8 +692,7 @@ def chattr(*files, **kwargs):
|
||||
result = __salt__['cmd.run'](cmd, python_shell=False)
|
||||
|
||||
if bool(result):
|
||||
raise CommandExecutionError(
|
||||
"chattr failed to run, possibly due to bad parameters.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@ -4641,19 +4652,6 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False,
|
||||
|
||||
is_dir = os.path.isdir(name)
|
||||
is_link = os.path.islink(name)
|
||||
if attrs is not None \
|
||||
and not salt.utils.platform.is_windows() \
|
||||
and not is_dir and not is_link:
|
||||
try:
|
||||
lattrs = lsattr(name)
|
||||
except SaltInvocationError:
|
||||
lattrs = None
|
||||
if lattrs is not None:
|
||||
# List attributes on file
|
||||
perms['lattrs'] = ''.join(lattrs.get(name, ''))
|
||||
# Remove attributes on file so changes can be enforced.
|
||||
if perms['lattrs']:
|
||||
chattr(name, operator='remove', attributes=perms['lattrs'])
|
||||
|
||||
# user/group changes if needed, then check if it worked
|
||||
if user:
|
||||
@ -4735,11 +4733,6 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False,
|
||||
elif 'cgroup' in perms and user != '':
|
||||
ret['changes']['group'] = group
|
||||
|
||||
if not salt.utils.platform.is_windows() and not is_dir:
|
||||
# Replace attributes on file if it had been removed
|
||||
if perms.get('lattrs', ''):
|
||||
chattr(name, operator='add', attributes=perms['lattrs'])
|
||||
|
||||
# Mode changes if needed
|
||||
if mode is not None:
|
||||
# File is a symlink, ignore the mode setting
|
||||
@ -4769,23 +4762,37 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False,
|
||||
pass
|
||||
else:
|
||||
diff_attrs = _cmp_attrs(name, attrs)
|
||||
if diff_attrs is not None:
|
||||
if diff_attrs[0] is not None or diff_attrs[1] is not None:
|
||||
if __opts__['test'] is True:
|
||||
ret['changes']['attrs'] = attrs
|
||||
if diff_attrs and any(attr for attr in diff_attrs):
|
||||
changes = {
|
||||
'old': ''.join(lsattr(name)[name]),
|
||||
'new': None,
|
||||
}
|
||||
if __opts__['test'] is True:
|
||||
changes['new'] = attrs
|
||||
else:
|
||||
if diff_attrs.added:
|
||||
chattr(
|
||||
name,
|
||||
operator="add",
|
||||
attributes=diff_attrs.added,
|
||||
)
|
||||
if diff_attrs.removed:
|
||||
chattr(
|
||||
name,
|
||||
operator="remove",
|
||||
attributes=diff_attrs.removed,
|
||||
)
|
||||
cmp_attrs = _cmp_attrs(name, attrs)
|
||||
if any(attr for attr in cmp_attrs):
|
||||
ret['result'] = False
|
||||
ret['comment'].append(
|
||||
'Failed to change attributes to {0}'.format(attrs)
|
||||
)
|
||||
changes['new'] = ''.join(lsattr(name)[name])
|
||||
else:
|
||||
if diff_attrs[0] is not None:
|
||||
chattr(name, operator="add", attributes=diff_attrs[0])
|
||||
if diff_attrs[1] is not None:
|
||||
chattr(name, operator="remove", attributes=diff_attrs[1])
|
||||
cmp_attrs = _cmp_attrs(name, attrs)
|
||||
if cmp_attrs[0] is not None or cmp_attrs[1] is not None:
|
||||
ret['result'] = False
|
||||
ret['comment'].append(
|
||||
'Failed to change attributes to {0}'.format(attrs)
|
||||
)
|
||||
else:
|
||||
ret['changes']['attrs'] = attrs
|
||||
changes['new'] = attrs
|
||||
if changes['old'] != changes['new']:
|
||||
ret['changes']['attrs'] = changes
|
||||
|
||||
# Set selinux attributes if needed
|
||||
if salt.utils.platform.is_linux() and (seuser or serole or setype or serange):
|
||||
|
@ -69,9 +69,10 @@ HAS_WINDOWS_MODULES = False
|
||||
try:
|
||||
if salt.utils.platform.is_windows():
|
||||
import win32api
|
||||
import win32file
|
||||
import win32con
|
||||
from pywintypes import error as pywinerror
|
||||
import win32file
|
||||
import win32security
|
||||
import salt.platform.win
|
||||
HAS_WINDOWS_MODULES = True
|
||||
except ImportError:
|
||||
HAS_WINDOWS_MODULES = False
|
||||
@ -1148,10 +1149,19 @@ def symlink(src, link):
|
||||
|
||||
is_dir = os.path.isdir(src)
|
||||
|
||||
# Elevate the token from the current process
|
||||
desired_access = (
|
||||
win32security.TOKEN_QUERY |
|
||||
win32security.TOKEN_ADJUST_PRIVILEGES
|
||||
)
|
||||
th = win32security.OpenProcessToken(win32api.GetCurrentProcess(),
|
||||
desired_access)
|
||||
salt.platform.win.elevate_token(th)
|
||||
|
||||
try:
|
||||
win32file.CreateSymbolicLink(link, src, int(is_dir))
|
||||
return True
|
||||
except pywinerror as exc:
|
||||
except win32file.error as exc:
|
||||
raise CommandExecutionError(
|
||||
'Could not create \'{0}\' - [{1}] {2}'.format(
|
||||
link,
|
||||
|
@ -145,6 +145,24 @@ instances = {'Parallel': TASK_INSTANCES_PARALLEL,
|
||||
'No New Instance': TASK_INSTANCES_IGNORE_NEW,
|
||||
'Stop Existing': TASK_INSTANCES_STOP_EXISTING}
|
||||
|
||||
results = {0x0: 'The operation completed successfully',
|
||||
0x1: 'Incorrect or unknown function called',
|
||||
0x2: 'File not found',
|
||||
0xA: 'The environment is incorrect',
|
||||
0x41300: 'Task is ready to run at its next scheduled time',
|
||||
0x41301: 'Task is currently running',
|
||||
0x41302: 'Task is disabled',
|
||||
0x41303: 'Task has not yet run',
|
||||
0x41304: 'There are no more runs scheduled for this task',
|
||||
0x41306: 'Task was terminated by the user',
|
||||
0x8004130F: 'Credentials became corrupted',
|
||||
0x8004131F: 'An instance of this task is already running',
|
||||
0x800710E0: 'The operator or administrator has refused the request',
|
||||
0x800704DD: 'The service is not available (Run only when logged '
|
||||
'in?)',
|
||||
0xC000013A: 'The application terminated as a result of CTRL+C',
|
||||
0xC06D007E: 'Unknown software exception'}
|
||||
|
||||
|
||||
def show_win32api_code(code):
|
||||
'''
|
||||
@ -2036,7 +2054,7 @@ def add_trigger(name=None,
|
||||
|
||||
*MonthlyDay*
|
||||
|
||||
The task will run monthly an the specified day.
|
||||
The task will run monthly on the specified day.
|
||||
|
||||
months_of_year (list):
|
||||
Sets the months of the year during which the task runs. Should
|
||||
|
@ -1146,7 +1146,7 @@ def dup_token(th):
|
||||
|
||||
def elevate_token(th):
|
||||
'''
|
||||
Set all token priviledges to enabled
|
||||
Set all token privileges to enabled
|
||||
'''
|
||||
# Get list of privileges this token contains
|
||||
privileges = win32security.GetTokenInformation(
|
||||
|
@ -67,7 +67,7 @@ if HAS_PIP is True:
|
||||
from pip._internal.exceptions import InstallationError # pylint: disable=E0611,E0401
|
||||
elif salt.utils.versions.compare(ver1=pip.__version__,
|
||||
oper='>=',
|
||||
ver2='10.0'):
|
||||
ver2='1.0'):
|
||||
from pip.exceptions import InstallationError # pylint: disable=E0611,E0401
|
||||
else:
|
||||
InstallationError = ValueError
|
||||
|
@ -261,11 +261,15 @@ class IPCClient(object):
|
||||
self.socket_path = socket_path
|
||||
self._closing = False
|
||||
self.stream = None
|
||||
if six.PY2:
|
||||
encoding = None
|
||||
# msgpack deprecated `encoding` starting with version 0.5.2
|
||||
if msgpack.version >= (0, 5, 2):
|
||||
msgpack_kwargs = {'raw': False}
|
||||
else:
|
||||
encoding = 'utf-8'
|
||||
self.unpacker = msgpack.Unpacker(encoding=encoding)
|
||||
if six.PY2:
|
||||
msgpack_kwargs = {'encoding': None}
|
||||
else:
|
||||
msgpack_kwargs = {'encoding': 'utf-8'}
|
||||
self.unpacker = msgpack.Unpacker(**msgpack_kwargs)
|
||||
|
||||
def connected(self):
|
||||
return self.stream is not None and not self.stream.closed()
|
||||
|
@ -25,6 +25,7 @@ except ImportError:
|
||||
|
||||
try:
|
||||
# Windows does not have the crypt module
|
||||
# consider using passlib.hash instead
|
||||
import crypt
|
||||
HAS_CRYPT = True
|
||||
except ImportError:
|
||||
@ -54,7 +55,7 @@ def secure_password(length=20, use_random=True):
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
pw += re.sub(
|
||||
salt.utils.stringutils.to_str(r'\W'),
|
||||
salt.utils.stringutils.to_str(r'[\W_]'),
|
||||
str(), # future lint: disable=blacklisted-function
|
||||
char
|
||||
)
|
||||
|
@ -3,7 +3,9 @@
|
||||
- system_site_packages: False
|
||||
- distribute: True
|
||||
{#- Provide the real path for the python executable in case tests are running inside a virtualenv #}
|
||||
{%- if salt.runtests_helpers.get_python_executable() %}
|
||||
- python: {{ salt.runtests_helpers.get_python_executable() }}
|
||||
{%- endif %}
|
||||
|
||||
pep8-pip:
|
||||
pip.installed:
|
||||
|
@ -6,7 +6,9 @@
|
||||
- system_site_packages: False
|
||||
- distribute: True
|
||||
{#- Provide the real path for the python executable in case tests are running inside a virtualenv #}
|
||||
{%- if salt.runtests_helpers.get_python_executable() %}
|
||||
- python: {{ salt.runtests_helpers.get_python_executable() }}
|
||||
{%- endif %}
|
||||
|
||||
install_older_venv_1:
|
||||
pip.installed:
|
||||
|
@ -55,10 +55,6 @@ def setup_handlers():
|
||||
pass
|
||||
sock.close()
|
||||
|
||||
# One million log messages is more than enough to queue.
|
||||
# Above that value, if `process_queue` can't process fast enough,
|
||||
# start dropping. This will contain a memory leak in case `process_queue`
|
||||
# can't process fast enough of in case it can't deliver the log records at all.
|
||||
if is_darwin():
|
||||
queue_size = 32767
|
||||
else:
|
||||
|
@ -2252,6 +2252,7 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
for id in _expected:
|
||||
self.assertEqual(sls[id]['comment'], _expected[id]['comment'])
|
||||
|
||||
@skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3')
|
||||
def test_state_sls_unicode_characters(self):
|
||||
'''
|
||||
test state.sls when state file contains non-ascii characters
|
||||
@ -2262,6 +2263,7 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
_expected = "cmd_|-echo1_|-echo 'This is Æ test!'_|-run"
|
||||
self.assertIn(_expected, ret)
|
||||
|
||||
@skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3')
|
||||
def test_state_sls_unicode_characters_cmd_output(self):
|
||||
'''
|
||||
test the output from running and echo command with non-ascii
|
||||
|
@ -48,7 +48,7 @@ class ManageTest(ShellCase):
|
||||
)
|
||||
# Make sure we can see the new key
|
||||
expected = 'Passed invalid arguments:'
|
||||
self.assertIn(expected, ret['return'])
|
||||
self.assertRaisesRegex(TypeError, expected)
|
||||
|
||||
def test_grains(self):
|
||||
'''
|
||||
|
@ -36,7 +36,7 @@ class ManageTest(ShellCase):
|
||||
'''
|
||||
ret = self.run_run_plus('jobs.lookup_jid')
|
||||
expected = 'Passed invalid arguments:'
|
||||
self.assertIn(expected, ret['return'])
|
||||
self.assertRaises(TypeError, expected)
|
||||
|
||||
@skipIf(True, 'to be re-enabled when #23623 is merged')
|
||||
def test_list_jobs(self):
|
||||
|
@ -32,4 +32,4 @@ class SaltRunnerTest(ShellCase):
|
||||
'''
|
||||
ret = self.run_run_plus('salt.cmd')
|
||||
expected = 'Passed invalid arguments:'
|
||||
self.assertIn(expected, ret['return'])
|
||||
self.assertRaisesRegex(TypeError, expected)
|
||||
|
@ -49,6 +49,7 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
|
||||
'subminion',
|
||||
)
|
||||
|
||||
@skipIf(salt.utils.platform.is_darwin(), 'Test is flaky on macosx')
|
||||
def test_issue_7754(self):
|
||||
old_cwd = os.getcwd()
|
||||
config_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-7754')
|
||||
|
@ -29,6 +29,9 @@ class CronTest(ModuleCase):
|
||||
'''
|
||||
self.run_state('user.present', name='test_cron_user')
|
||||
|
||||
self.user_primary_group = self.run_function('user.primary_group',
|
||||
name='test_cron_user')
|
||||
|
||||
def tearDown(self):
|
||||
'''
|
||||
Teardown
|
||||
@ -45,7 +48,7 @@ class CronTest(ModuleCase):
|
||||
_expected = {
|
||||
'changes': {
|
||||
'diff': '--- \n+++ \n@@ -1 +1,2 @@\n-\n+# Lines below here are managed by Salt, do not edit\n+@hourly touch /tmp/test-file\n',
|
||||
'group': user_id,
|
||||
'group': self.user_primary_group,
|
||||
'user': user_id,
|
||||
},
|
||||
}
|
||||
|
@ -2137,6 +2137,367 @@ class FileBasicsTestCase(TestCase, LoaderModuleMockMixin):
|
||||
self.assertEqual(list(ret), ['file://' + self.myfile, 'filehash'])
|
||||
|
||||
|
||||
class LsattrTests(TestCase, LoaderModuleMockMixin):
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
filemod: {
|
||||
'__salt__': {
|
||||
'cmd.run': cmdmod.run,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def run(self, result=None):
|
||||
patch_aix = patch(
|
||||
'salt.utils.platform.is_aix',
|
||||
Mock(return_value=False),
|
||||
)
|
||||
patch_exists = patch(
|
||||
'os.path.exists',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value='fnord'),
|
||||
)
|
||||
with patch_aix, patch_exists, patch_which:
|
||||
super(LsattrTests, self).run(result)
|
||||
|
||||
def test_if_lsattr_is_missing_it_should_return_None(self):
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value=None),
|
||||
)
|
||||
with patch_which:
|
||||
actual = filemod.lsattr('foo')
|
||||
assert actual is None, actual
|
||||
|
||||
def test_on_aix_lsattr_should_be_None(self):
|
||||
patch_aix = patch(
|
||||
'salt.utils.platform.is_aix',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
with patch_aix:
|
||||
# SaltInvocationError will be raised if filemod.lsattr
|
||||
# doesn't early exit
|
||||
actual = filemod.lsattr('foo')
|
||||
self.assertIsNone(actual)
|
||||
|
||||
def test_SaltInvocationError_should_be_raised_when_file_is_missing(self):
|
||||
patch_exists = patch(
|
||||
'os.path.exists',
|
||||
Mock(return_value=False),
|
||||
)
|
||||
with patch_exists, self.assertRaises(SaltInvocationError):
|
||||
filemod.lsattr('foo')
|
||||
|
||||
def test_if_chattr_version_is_less_than_required_flags_should_ignore_extended(self):
|
||||
fname = '/path/to/fnord'
|
||||
with_extended = textwrap.dedent(
|
||||
'''
|
||||
aAcCdDeijPsStTu---- {}
|
||||
'''
|
||||
).strip().format(fname)
|
||||
expected = set('acdijstuADST')
|
||||
patch_has_ext = patch(
|
||||
'salt.modules.file._chattr_has_extended_attrs',
|
||||
Mock(return_value=False),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': Mock(return_value=with_extended)},
|
||||
)
|
||||
with patch_has_ext, patch_run:
|
||||
actual = set(filemod.lsattr(fname)[fname])
|
||||
msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322
|
||||
assert actual == expected, msg
|
||||
|
||||
def test_if_chattr_version_is_high_enough_then_extended_flags_should_be_returned(self):
|
||||
fname = '/path/to/fnord'
|
||||
with_extended = textwrap.dedent(
|
||||
'''
|
||||
aAcCdDeijPsStTu---- {}
|
||||
'''
|
||||
).strip().format(fname)
|
||||
expected = set('aAcCdDeijPsStTu')
|
||||
patch_has_ext = patch(
|
||||
'salt.modules.file._chattr_has_extended_attrs',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': Mock(return_value=with_extended)},
|
||||
)
|
||||
with patch_has_ext, patch_run:
|
||||
actual = set(filemod.lsattr(fname)[fname])
|
||||
msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322
|
||||
assert actual == expected, msg
|
||||
|
||||
def test_if_supports_extended_but_there_are_no_flags_then_none_should_be_returned(self):
|
||||
fname = '/path/to/fnord'
|
||||
with_extended = textwrap.dedent(
|
||||
'''
|
||||
------------------- {}
|
||||
'''
|
||||
).strip().format(fname)
|
||||
expected = set('')
|
||||
patch_has_ext = patch(
|
||||
'salt.modules.file._chattr_has_extended_attrs',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': Mock(return_value=with_extended)},
|
||||
)
|
||||
with patch_has_ext, patch_run:
|
||||
actual = set(filemod.lsattr(fname)[fname])
|
||||
msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322
|
||||
assert actual == expected, msg
|
||||
|
||||
|
||||
class ChattrTests(TestCase, LoaderModuleMockMixin):
|
||||
def setup_loader_modules(self):
|
||||
return {
|
||||
filemod: {
|
||||
'__salt__': {
|
||||
'cmd.run': cmdmod.run,
|
||||
},
|
||||
'__opts__': {
|
||||
'test': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def run(self, result=None):
|
||||
patch_aix = patch(
|
||||
'salt.utils.platform.is_aix',
|
||||
Mock(return_value=False),
|
||||
)
|
||||
patch_exists = patch(
|
||||
'os.path.exists',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value='some/tune2fs'),
|
||||
)
|
||||
with patch_aix, patch_exists, patch_which:
|
||||
super(ChattrTests, self).run(result)
|
||||
|
||||
def test_chattr_version_returns_None_if_no_tune2fs_exists(self):
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value=''),
|
||||
)
|
||||
with patch_which:
|
||||
actual = filemod._chattr_version()
|
||||
self.assertIsNone(actual)
|
||||
|
||||
def test_on_aix_chattr_version_should_be_None_even_if_tune2fs_exists(self):
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value='fnord'),
|
||||
)
|
||||
patch_aix = patch(
|
||||
'salt.utils.platform.is_aix',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
mock_run = MagicMock(return_value='fnord')
|
||||
patch_run = patch.dict(filemod.__salt__, {'cmd.run': mock_run})
|
||||
with patch_which, patch_aix, patch_run:
|
||||
actual = filemod._chattr_version()
|
||||
self.assertIsNone(actual)
|
||||
mock_run.assert_not_called()
|
||||
|
||||
def test_chattr_version_should_return_version_from_tune2fs(self):
|
||||
expected = '1.43.4'
|
||||
sample_output = textwrap.dedent(
|
||||
'''
|
||||
tune2fs 1.43.4 (31-Jan-2017)
|
||||
Usage: tune2fs [-c max_mounts_count] [-e errors_behavior] [-f] [-g group]
|
||||
[-i interval[d|m|w]] [-j] [-J journal_options] [-l]
|
||||
[-m reserved_blocks_percent] [-o [^]mount_options[,...]]
|
||||
[-p mmp_update_interval] [-r reserved_blocks_count] [-u user]
|
||||
[-C mount_count] [-L volume_label] [-M last_mounted_dir]
|
||||
[-O [^]feature[,...]] [-Q quota_options]
|
||||
[-E extended-option[,...]] [-T last_check_time] [-U UUID]
|
||||
[-I new_inode_size] [-z undo_file] device
|
||||
'''
|
||||
)
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value='fnord'),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': MagicMock(return_value=sample_output)},
|
||||
)
|
||||
with patch_which, patch_run:
|
||||
actual = filemod._chattr_version()
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_if_tune2fs_has_no_version_version_should_be_None(self):
|
||||
patch_which = patch(
|
||||
'salt.utils.path.which',
|
||||
Mock(return_value='fnord'),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': MagicMock(return_value='fnord')},
|
||||
)
|
||||
with patch_which, patch_run:
|
||||
actual = filemod._chattr_version()
|
||||
self.assertIsNone(actual)
|
||||
|
||||
def test_chattr_has_extended_attrs_should_return_False_if_chattr_version_is_None(self):
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=None),
|
||||
)
|
||||
with patch_chattr:
|
||||
actual = filemod._chattr_has_extended_attrs()
|
||||
assert not actual, actual
|
||||
|
||||
def test_chattr_has_extended_attrs_should_return_False_if_version_is_too_low(self):
|
||||
below_expected = '0.1.1'
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=below_expected),
|
||||
)
|
||||
with patch_chattr:
|
||||
actual = filemod._chattr_has_extended_attrs()
|
||||
assert not actual, actual
|
||||
|
||||
def test_chattr_has_extended_attrs_should_return_False_if_version_is_equal_threshold(self):
|
||||
threshold = '1.41.12'
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=threshold),
|
||||
)
|
||||
with patch_chattr:
|
||||
actual = filemod._chattr_has_extended_attrs()
|
||||
assert not actual, actual
|
||||
|
||||
def test_chattr_has_extended_attrs_should_return_True_if_version_is_above_threshold(self):
|
||||
higher_than = '1.41.13'
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=higher_than),
|
||||
)
|
||||
with patch_chattr:
|
||||
actual = filemod._chattr_has_extended_attrs()
|
||||
assert actual, actual
|
||||
|
||||
def test_check_perms_should_report_no_attr_changes_if_there_are_none(self):
|
||||
filename = '/path/to/fnord'
|
||||
attrs = 'aAcCdDeijPsStTu'
|
||||
|
||||
higher_than = '1.41.13'
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=higher_than),
|
||||
)
|
||||
patch_exists = patch(
|
||||
'os.path.exists',
|
||||
Mock(return_value=True),
|
||||
)
|
||||
patch_stats = patch(
|
||||
'salt.modules.file.stats',
|
||||
Mock(return_value={
|
||||
'user': 'foo',
|
||||
'group': 'bar',
|
||||
'mode': '123',
|
||||
}),
|
||||
)
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': MagicMock(return_value='--------- '+filename)},
|
||||
)
|
||||
with patch_chattr, patch_exists, patch_stats, patch_run:
|
||||
actual_ret, actual_perms = filemod.check_perms(
|
||||
name=filename,
|
||||
ret=None,
|
||||
user='foo',
|
||||
group='bar',
|
||||
mode='123',
|
||||
attrs=attrs,
|
||||
follow_symlinks=False,
|
||||
)
|
||||
assert actual_ret.get('changes', {}).get('attrs')is None, actual_ret
|
||||
|
||||
def test_check_perms_should_report_attrs_new_and_old_if_they_changed(self):
|
||||
filename = '/path/to/fnord'
|
||||
attrs = 'aAcCdDeijPsStTu'
|
||||
existing_attrs = 'aeiu'
|
||||
expected = {
|
||||
'attrs': {
|
||||
'old': existing_attrs,
|
||||
'new': attrs,
|
||||
},
|
||||
}
|
||||
|
||||
higher_than = '1.41.13'
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file._chattr_version',
|
||||
Mock(return_value=higher_than),
|
||||
)
|
||||
patch_stats = patch(
|
||||
'salt.modules.file.stats',
|
||||
Mock(return_value={
|
||||
'user': 'foo',
|
||||
'group': 'bar',
|
||||
'mode': '123',
|
||||
}),
|
||||
)
|
||||
patch_cmp = patch(
|
||||
'salt.modules.file._cmp_attrs',
|
||||
MagicMock(side_effect=[
|
||||
filemod.AttrChanges(
|
||||
added='aAcCdDeijPsStTu',
|
||||
removed='',
|
||||
),
|
||||
filemod.AttrChanges(
|
||||
None,
|
||||
None,
|
||||
),
|
||||
]),
|
||||
)
|
||||
patch_chattr = patch(
|
||||
'salt.modules.file.chattr',
|
||||
MagicMock(),
|
||||
)
|
||||
|
||||
def fake_cmd(cmd, *args, **kwargs):
|
||||
if cmd == ['lsattr', '/path/to/fnord']:
|
||||
return textwrap.dedent(
|
||||
'''
|
||||
{}---- {}
|
||||
'''.format(existing_attrs, filename)
|
||||
).strip()
|
||||
else:
|
||||
assert False, "not sure how to handle {}".format(cmd)
|
||||
|
||||
patch_run = patch.dict(
|
||||
filemod.__salt__,
|
||||
{'cmd.run': MagicMock(side_effect=fake_cmd)},
|
||||
)
|
||||
patch_ver = patch(
|
||||
'salt.modules.file._chattr_has_extended_attrs',
|
||||
MagicMock(return_value=True),
|
||||
)
|
||||
with patch_chattr, patch_stats, patch_cmp, patch_run, patch_ver:
|
||||
actual_ret, actual_perms = filemod.check_perms(
|
||||
name=filename,
|
||||
ret=None,
|
||||
user='foo',
|
||||
group='bar',
|
||||
mode='123',
|
||||
attrs=attrs,
|
||||
follow_symlinks=False,
|
||||
)
|
||||
self.assertDictEqual(actual_ret['changes'], expected)
|
||||
|
||||
|
||||
@skipIf(salt.modules.selinux.getenforce() != 'Enforcing', 'Skip if selinux not enabled')
|
||||
class FileSelinuxTestCase(TestCase, LoaderModuleMockMixin):
|
||||
def setup_loader_modules(self):
|
||||
@ -2207,34 +2568,3 @@ class FileSelinuxTestCase(TestCase, LoaderModuleMockMixin):
|
||||
result = filemod.check_perms(self.tfile3.name, {}, 'root', 'root', 644, seuser=None,
|
||||
serole=None, setype='lost_found_t', serange=None)
|
||||
self.assertEqual(result, expected_result)
|
||||
|
||||
|
||||
class ChattrVersionTests(TestCase):
|
||||
CHATTR_MAN = salt.utils.stringutils.to_bytes((
|
||||
'AVAILABILITY\n'
|
||||
'chattr is part of the e2fsprogs package and is available '
|
||||
'from http://e2fsprogs.sourceforge.net.\n'
|
||||
'SEE ALSO\n'
|
||||
' lsattr(1), btrfs(5), ext4(5), xfs(5).\n\n'
|
||||
'E2fsprogs version 1.43.4 '
|
||||
' '
|
||||
'January 2017 '
|
||||
' '
|
||||
' CHATTR(1)'
|
||||
))
|
||||
|
||||
def test__parse_chattr_version(self):
|
||||
'''
|
||||
Validate we can parse the E2fsprogs version from the chattr man page
|
||||
'''
|
||||
man_out = dedent(self.CHATTR_MAN)
|
||||
parsed_version = filemod._parse_chattr_man(man_out)
|
||||
assert parsed_version == '1.43.4', parsed_version
|
||||
|
||||
def test__chattr_version(self):
|
||||
'''
|
||||
The _chattr_version method works
|
||||
'''
|
||||
with patch('subprocess.check_output', return_value=self.CHATTR_MAN):
|
||||
parsed_version = filemod._chattr_version()
|
||||
assert parsed_version == '1.43.4', parsed_version
|
||||
|
@ -257,29 +257,6 @@ class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin):
|
||||
{'test': ret}
|
||||
)
|
||||
|
||||
# Test VCS installations with version info like >= 0.1
|
||||
with patch.object(pip, '__version__', MagicMock(side_effect=AttributeError(
|
||||
'Faked missing __version__ attribute'))):
|
||||
mock = MagicMock(return_value={'retcode': 0, 'stdout': ''})
|
||||
pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'})
|
||||
pip_install = MagicMock(return_value={
|
||||
'retcode': 0,
|
||||
'stderr': '',
|
||||
'stdout': 'Cloned!'
|
||||
})
|
||||
with patch.dict(pip_state.__salt__, {'cmd.run_all': mock,
|
||||
'pip.list': pip_list,
|
||||
'pip.install': pip_install}):
|
||||
with patch.dict(pip_state.__opts__, {'test': False}):
|
||||
ret = pip_state.installed(
|
||||
'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.0'
|
||||
)
|
||||
self.assertSaltTrueReturn({'test': ret})
|
||||
self.assertInSaltComment(
|
||||
'packages are already installed',
|
||||
{'test': ret}
|
||||
)
|
||||
|
||||
def test_install_in_editable_mode(self):
|
||||
'''
|
||||
Check that `name` parameter containing bad characters is not parsed by
|
||||
|
@ -1001,25 +1001,25 @@ class LoaderGlobalsTest(ModuleCase):
|
||||
Verify that the globals listed in the doc string (from the test) are in these modules
|
||||
'''
|
||||
# find the globals
|
||||
global_vars = []
|
||||
global_vars = {}
|
||||
for val in six.itervalues(mod_dict):
|
||||
# only find salty globals
|
||||
if val.__module__.startswith('salt.loaded'):
|
||||
if hasattr(val, '__globals__'):
|
||||
if hasattr(val, '__wrapped__') or '__wrapped__' in val.__globals__:
|
||||
global_vars.append(sys.modules[val.__module__].__dict__)
|
||||
global_vars[val.__module__] = sys.modules[val.__module__].__dict__
|
||||
else:
|
||||
global_vars.append(val.__globals__)
|
||||
global_vars[val.__module__] = val.__globals__
|
||||
|
||||
# if we couldn't find any, then we have no modules -- so something is broken
|
||||
self.assertNotEqual(global_vars, [], msg='No modules were loaded.')
|
||||
self.assertNotEqual(global_vars, {}, msg='No modules were loaded.')
|
||||
|
||||
# get the names of the globals you should have
|
||||
func_name = inspect.stack()[1][3]
|
||||
names = next(six.itervalues(salt.utils.yaml.safe_load(getattr(self, func_name).__doc__)))
|
||||
|
||||
# Now, test each module!
|
||||
for item in global_vars:
|
||||
for item in six.itervalues(global_vars):
|
||||
for name in names:
|
||||
self.assertIn(name, list(item.keys()))
|
||||
|
||||
|
44
tests/unit/utils/test_pycrypto.py
Normal file
44
tests/unit/utils/test_pycrypto.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import logging
|
||||
import re
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.utils.pycrypto
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PycryptoTestCase(TestCase):
|
||||
'''
|
||||
TestCase for salt.utils.pycrypto module
|
||||
'''
|
||||
|
||||
@skipIf(not salt.utils.pycrypto.HAS_CRYPT, 'No crypto library available')
|
||||
def test_gen_hash(self):
|
||||
'''
|
||||
Test gen_hash
|
||||
'''
|
||||
passwd = 'test_password'
|
||||
ret = salt.utils.pycrypto.gen_hash(password=passwd)
|
||||
self.assertTrue(ret.startswith('$6$'))
|
||||
|
||||
ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm='md5')
|
||||
self.assertTrue(ret.startswith('$1$'))
|
||||
|
||||
ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm='sha256')
|
||||
self.assertTrue(ret.startswith('$5$'))
|
||||
|
||||
def test_secure_password(self):
|
||||
'''
|
||||
test secure_password
|
||||
'''
|
||||
ret = salt.utils.pycrypto.secure_password()
|
||||
check = re.compile(r'[!@#$%^&*()_=+]')
|
||||
assert check.search(ret) is None
|
||||
assert ret
|
Loading…
Reference in New Issue
Block a user