Merge pull request #48431 from rallytime/merge-2018.3

[2018.3] Merge forward from 2017.7 to 2018.3
This commit is contained in:
Nicole Thomas 2018-07-05 13:22:59 -04:00 committed by GitHub
commit 2baa7f189f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 88 deletions

View file

@ -4722,6 +4722,11 @@ def check_managed_changes(
defaults,
skip_verify,
**kwargs)
# Ensure that user-provided hash string is lowercase
if source_sum and ('hsum' in source_sum):
source_sum['hsum'] = source_sum['hsum'].lower()
if comments:
__clean_tmp(sfn)
return False, comments

View file

@ -705,7 +705,7 @@ def modify(name,
win32service.SERVICE_QUERY_CONFIG)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed To Open {0}: {1}'.format(name, exc[2]))
'Failed To Open {0}: {1}'.format(name, exc))
config_info = win32service.QueryServiceConfig(handle_svc)

View file

@ -83,6 +83,8 @@ def _checksum_file_path(path):
drive.rstrip(':'),
path.lstrip('/\\'),
)
elif str(exc).startswith('Cannot mix UNC'):
relpath = salt.utils.path_join('unc', path)
else:
raise
ret = salt.utils.path.join(__opts__['cachedir'], 'archive_hash', relpath)

View file

@ -1422,19 +1422,25 @@ def symlink(
preflight_errors = []
if salt.utils.platform.is_windows():
# Make sure the passed owner exists
if not salt.utils.win_functions.get_sid_from_name(win_owner):
try:
salt.utils.win_functions.get_sid_from_name(win_owner)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(win_owner))
# Make sure users passed in win_perms exist
if win_perms:
for name_check in win_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
try:
salt.utils.win_functions.get_sid_from_name(name_check)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(name_check))
# Make sure users passed in win_deny_perms exist
if win_deny_perms:
for name_check in win_deny_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
try:
salt.utils.win_functions.get_sid_from_name(name_check)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(name_check))
else:
uid = __salt__['file.user_to_uid'](user)

View file

@ -0,0 +1 @@
Hello, World!

View file

@ -656,6 +656,57 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
self.assertIn(
'does not exist', ret['comment'])
def test_managed_source_hash_indifferent_case(self):
'''
Test passing a source_hash as an uppercase hash.
This is a regression test for Issue #38914 and Issue #48230 (test=true use).
'''
name = os.path.join(TMP, 'source_hash_indifferent_case')
state_name = 'file_|-/tmp/salt-tests-tmpdir/source_hash_indifferent_case_|' \
'-/tmp/salt-tests-tmpdir/source_hash_indifferent_case_|-managed'
local_path = os.path.join(FILES, 'file', 'base', 'hello_world.txt')
actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31'
uppercase_hash = actual_hash.upper()
try:
# Lay down tmp file to test against
self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=actual_hash
)
# Test uppercase source_hash: should return True with no changes
ret = self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=uppercase_hash
)
assert ret[state_name]['result'] is True
assert ret[state_name]['pchanges'] == {}
assert ret[state_name]['changes'] == {}
# Test uppercase source_hash using test=true
# Should return True with no changes
ret = self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=uppercase_hash,
test=True
)
assert ret[state_name]['result'] is True
assert ret[state_name]['pchanges'] == {}
assert ret[state_name]['changes'] == {}
finally:
# Clean Up File
if os.path.exists(name):
os.remove(name)
def test_directory(self):
'''
file.directory

View file

@ -36,6 +36,7 @@ import salt.serializers.yaml as yamlserializer
import salt.serializers.json as jsonserializer
import salt.serializers.python as pythonserializer
from salt.exceptions import CommandExecutionError
import salt.utils.win_functions
@skipIf(NO_MOCK, NO_MOCK_REASON)
@ -143,10 +144,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
else:
group = 'saltstack'
ret = {'name': name,
'result': False,
'comment': '',
'changes': {}}
def return_val(kwargs):
val = {
'name': name,
'result': False,
'comment': '',
'changes': {},
}
val.update(kwargs)
return val
mock_t = MagicMock(return_value=True)
mock_f = MagicMock(return_value=False)
@ -160,7 +166,7 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t}):
comt = ('Must provide name to file.symlink')
ret.update({'comment': comt, 'name': ''})
ret = return_val({'comment': comt, 'name': ''})
self.assertDictEqual(filestate.symlink('', target), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
@ -168,8 +174,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'file.group_to_gid': mock_empty,
'user.info': mock_empty,
'user.current': mock_user}):
comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group))
ret.update({'comment': comt, 'name': name})
if salt.utils.is_windows():
comt = ('User {0} does not exist'.format(user))
ret = return_val({'comment': comt, 'name': name})
else:
comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group))
ret = return_val({'comment': comt, 'name': name})
self.assertDictEqual(filestate.symlink(name, target, user=user,
group=group), ret)
@ -181,11 +191,22 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'user.current': mock_user}):
with patch.dict(filestate.__opts__, {'test': True}):
with patch.object(os.path, 'exists', mock_f):
comt = ('Symlink {0} to {1}'
' is set for creation').format(name, target)
ret.update({'comment': comt,
'result': None,
'pchanges': {'new': name}})
if salt.utils.is_windows():
comt = ('User {0} does not exist'.format(user))
ret = return_val(
{
'comment': comt,
'result': False,
'name': name,
'changes': {}
}
)
else:
comt = ('Symlink {0} to {1}'
' is set for creation').format(name, target)
ret = return_val({'comment': comt,
'result': None,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
@ -199,10 +220,21 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', mock_f):
with patch.object(os.path, 'exists', mock_f):
comt = ('Directory {0} for symlink is not present').format(test_dir)
ret.update({'comment': comt,
if salt.utils.is_windows():
comt = 'User {0} does not exist'.format(user)
ret = return_val(
{
'comment': comt,
'result': False,
'pchanges': {'new': name}})
'name': name,
'changes': {},
}
)
else:
comt = ('Directory {0} for symlink is not present').format(test_dir)
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
@ -217,15 +249,19 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(salt.states.file, '_check_symlink_ownership', mock_t):
comt = ('Symlink {0} is present and owned by '
'{1}:{2}'.format(name, user, group))
ret.update({'comment': comt,
'result': True,
'pchanges': {}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
if salt.utils.is_windows():
comt = ('Symlink {0} is present and owned by '
'{1}'.format(name, user))
else:
comt = ('Symlink {0} is present and owned by '
'{1}:{2}'.format(name, user, group))
ret = return_val({'comment': comt,
'result': True,
'pchanges': {}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
'file.group_to_gid': mock_gid,
@ -237,15 +273,16 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
with patch.object(os.path, 'lexists', mock_t):
comt = ('File exists where the backup target SALT'
' should go')
ret.update({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group, backupname='SALT'),
ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the backup target SALT'
' should go')
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group, backupname='SALT'),
ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -258,14 +295,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
with patch.object(os.path, 'isfile', mock_t):
comt = ('File exists where the symlink {0} should be'
.format(name))
ret.update({'comment': comt,
'pchanges': {'new': name},
'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the symlink {0} should be'
.format(name))
ret = return_val({'comment': comt,
'pchanges': {'new': name},
'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -279,11 +317,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_t):
with patch.object(os.path, 'exists', mock_f):
comt = ('File exists where the symlink {0} should be'.format(name))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the symlink {0} should be'.format(name))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -297,11 +336,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
comt = ('Directory exists where the symlink {0} should be'.format(name))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('Directory exists where the symlink {0} should be'.format(name))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -314,12 +354,13 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = ('Unable to create new symlink {0} -> '
'{1}: '.format(name, target))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('Unable to create new symlink {0} -> '
'{1}: '.format(name, target))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -334,13 +375,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = 'Created new symlink {0} -> {1}'.format(name, target)
ret.update({'comment': comt,
'result': True,
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.states.file._check_symlink_ownership', return_value=True):
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = 'Created new symlink {0} -> {1}'.format(name, target)
ret = return_val({'comment': comt,
'result': True, 'pchanges': {'new': '/tmp/testfile.txt'},
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -355,15 +398,19 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = ('Created new symlink {0} -> {1}, '
'but was unable to set ownership to '
'{2}:{3}'.format(name, target, user, group))
ret.update({'comment': comt,
'result': False,
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
with patch('salt.states.file._set_symlink_ownership', return_value=False):
with patch('salt.states.file._check_symlink_ownership', return_value=False):
comt = ('Created new symlink {0} -> {1}, '
'but was unable to set ownership to '
'{2}:{3}'.format(name, target, user, group))
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': '/tmp/testfile.txt'},
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
# 'absent' function tests: 1
def test_absent(self):
@ -1131,6 +1178,8 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
Test to ensure that some text appears at the beginning of a file.
'''
name = '/etc/motd'
if salt.utils.is_windows():
name = 'c:\\etc\\motd'
source = ['salt://motd/hr-messages.tmpl']
sources = ['salt://motd/devops-messages.tmpl']
text = ['Trust no one unless you have eaten much salt with him.']
@ -1159,12 +1208,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'cp.get_template': mock_f,
'file.search': mock_f,
'file.prepend': mock_t}):
with patch.object(os.path, 'isdir', mock_t):
comt = ('The following files will be changed:\n/etc:'
' directory - new\n')
ret.update({'comment': comt, 'name': name, 'pchanges': {'/etc': {'directory': 'new'}}})
self.assertDictEqual(filestate.prepend(name, makedirs=True),
ret)
comt = ('The following files will be changed:\n/etc:'
' directory - new\n')
pchanges = {'/etc': {'directory': 'new'}}
if salt.utils.is_windows():
comt = 'The directory "c:\\etc" will be changed'
pchanges = {'c:\\etc': {'directory': 'new'}}
ret.update({'comment': comt, 'name': name, 'pchanges': pchanges})
self.assertDictEqual(filestate.prepend(name, makedirs=True),
ret)
with patch.object(os.path, 'isabs', mock_f):
comt = ('Specified file {0} is not an absolute path'

View file

@ -125,6 +125,7 @@ class ServiceTestCase(TestCase, LoaderModuleMockMixin):
{'changes': 'saltstack',
'comment': 'The service salt is already dead', 'name': 'salt',
'result': True}]
info_mock = MagicMock(return_value={'StartType': ''})
mock = MagicMock(return_value="salt")
with patch.object(service, '_enabled_used_error', mock):
@ -140,31 +141,36 @@ class ServiceTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(service.__opts__, {'test': True}):
with patch.dict(service.__salt__, {'service.enabled': fmock,
'service.stop': tmock,
'service.status': fmock}):
'service.status': fmock,
'service.info': info_mock}):
with patch.object(service, '_enable', mock):
self.assertDictEqual(service.dead("salt", True), ret[5])
with patch.dict(service.__salt__, {'service.enabled': tmock,
'service.status': tmock}):
'service.status': tmock,
'service.info': info_mock}):
self.assertDictEqual(service.dead("salt"), ret[2])
with patch.dict(service.__opts__, {'test': False}):
with patch.dict(service.__salt__, {'service.enabled': fmock,
'service.stop': tmock,
'service.status': fmock}):
'service.status': fmock,
'service.info': info_mock}):
with patch.object(service, '_enable', mock):
self.assertDictEqual(service.dead("salt", True), ret[1])
with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[True, True, False]),
'service.status': MagicMock(side_effect=[True, False, False]),
'service.stop': MagicMock(return_value="stack")}):
'service.stop': MagicMock(return_value="stack"),
'service.info': info_mock}):
with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})):
self.assertDictEqual(service.dead("salt", True), ret[3])
# test an initd which a wrong status (True even if dead)
with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[False, False, False]),
'service.status': MagicMock(side_effect=[True, True, True]),
'service.stop': MagicMock(return_value="stack")}):
'service.stop': MagicMock(return_value="stack"),
'service.info': info_mock}):
with patch.object(service, '_disable', MagicMock(return_value={})):
self.assertDictEqual(service.dead("salt", False), ret[4])