mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Merge pull request #37647 from americanexpress/cron-non-root
Revising cron for OS agnostic support of cron when running non-root
This commit is contained in:
commit
c25a306759
@ -156,14 +156,20 @@ def _get_cron_cmdstr(path, user=None):
|
||||
'''
|
||||
Returns a format string, to be used to build a crontab command.
|
||||
'''
|
||||
cmd = 'crontab'
|
||||
|
||||
if user and __grains__.get('os_family') not in ('Solaris', 'AIX'):
|
||||
cmd += ' -u {0}'.format(user)
|
||||
|
||||
if user:
|
||||
cmd = 'crontab -u {0}'.format(user)
|
||||
else:
|
||||
cmd = 'crontab'
|
||||
return '{0} {1}'.format(cmd, path)
|
||||
|
||||
|
||||
def _check_instance_uid_match(user):
|
||||
'''
|
||||
Returns true if running instance's UID matches the specified user UID
|
||||
'''
|
||||
return os.geteuid() == __salt__['file.user_to_uid'](user)
|
||||
|
||||
|
||||
def write_cron_file(user, path):
|
||||
'''
|
||||
Writes the contents of a file to a user's crontab
|
||||
@ -178,11 +184,10 @@ def write_cron_file(user, path):
|
||||
|
||||
.. note::
|
||||
|
||||
Solaris and AIX require that `path` is readable by `user`
|
||||
Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX)
|
||||
'''
|
||||
appUser = __opts__['user']
|
||||
if __grains__.get('os_family') in ('Solaris', 'AIX') and appUser != user:
|
||||
return __salt__['cmd.retcode'](_get_cron_cmdstr(path, user),
|
||||
if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'):
|
||||
return __salt__['cmd.retcode'](_get_cron_cmdstr(path),
|
||||
runas=user,
|
||||
python_shell=False) == 0
|
||||
else:
|
||||
@ -204,11 +209,10 @@ def write_cron_file_verbose(user, path):
|
||||
|
||||
.. note::
|
||||
|
||||
Solaris and AIX require that `path` is readable by `user`
|
||||
Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX)
|
||||
'''
|
||||
appUser = __opts__['user']
|
||||
if __grains__.get('os_family') in ('Solaris', 'AIX') and appUser != user:
|
||||
return __salt__['cmd.run_all'](_get_cron_cmdstr(path, user),
|
||||
if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'):
|
||||
return __salt__['cmd.run_all'](_get_cron_cmdstr(path),
|
||||
runas=user,
|
||||
python_shell=False)
|
||||
else:
|
||||
@ -220,13 +224,12 @@ def _write_cron_lines(user, lines):
|
||||
'''
|
||||
Takes a list of lines to be committed to a user's crontab and writes it
|
||||
'''
|
||||
appUser = __opts__['user']
|
||||
path = salt.utils.files.mkstemp()
|
||||
if __grains__.get('os_family') in ('Solaris', 'AIX') and appUser != user:
|
||||
# on solaris/aix we change to the user before executing the commands
|
||||
if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'):
|
||||
# In some cases crontab command should be executed as user rather than root
|
||||
with salt.utils.fpopen(path, 'w+', uid=__salt__['file.user_to_uid'](user), mode=0o600) as fp_:
|
||||
fp_.writelines(lines)
|
||||
ret = __salt__['cmd.run_all'](_get_cron_cmdstr(path, user),
|
||||
ret = __salt__['cmd.run_all'](_get_cron_cmdstr(path),
|
||||
runas=user,
|
||||
python_shell=False)
|
||||
else:
|
||||
@ -234,7 +237,6 @@ def _write_cron_lines(user, lines):
|
||||
fp_.writelines(lines)
|
||||
ret = __salt__['cmd.run_all'](_get_cron_cmdstr(path, user),
|
||||
python_shell=False)
|
||||
|
||||
os.remove(path)
|
||||
return ret
|
||||
|
||||
@ -259,28 +261,20 @@ def raw_cron(user):
|
||||
|
||||
salt '*' cron.raw_cron root
|
||||
'''
|
||||
|
||||
appUser = __opts__['user']
|
||||
if __grains__.get('os_family') in ('Solaris', 'AIX'):
|
||||
if appUser == user:
|
||||
cmd = 'crontab -l'
|
||||
else:
|
||||
cmd = 'crontab -l {0}'.format(user)
|
||||
if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'):
|
||||
cmd = 'crontab -l'
|
||||
# Preserve line endings
|
||||
lines = __salt__['cmd.run_stdout'](cmd,
|
||||
runas=user,
|
||||
rstrip=False,
|
||||
python_shell=False).splitlines(True)
|
||||
else:
|
||||
if appUser == user:
|
||||
cmd = 'crontab -l'
|
||||
else:
|
||||
cmd = 'crontab -l -u {0}'.format(user)
|
||||
cmd = 'crontab -u {0} -l'.format(user)
|
||||
# Preserve line endings
|
||||
lines = __salt__['cmd.run_stdout'](cmd,
|
||||
ignore_retcode=True,
|
||||
rstrip=False,
|
||||
python_shell=False).splitlines(True)
|
||||
|
||||
if len(lines) != 0 and lines[0].startswith('# DO NOT EDIT THIS FILE - edit the master and reinstall.'):
|
||||
del lines[0:3]
|
||||
return ''.join(lines)
|
||||
|
@ -511,7 +511,15 @@ def file(name,
|
||||
'''
|
||||
# Initial set up
|
||||
mode = salt.utils.normalize_mode('0600')
|
||||
owner, group, crontab_dir = _get_cron_info()
|
||||
|
||||
try:
|
||||
group = __salt__['user.info'](user)['groups'][0]
|
||||
except Exception:
|
||||
ret = {'changes': {},
|
||||
'comment': "Could not identify group for user {0}".format(user),
|
||||
'name': name,
|
||||
'result': False}
|
||||
return ret
|
||||
|
||||
cron_path = salt.utils.files.mkstemp()
|
||||
with salt.utils.fopen(cron_path, 'w+') as fp_:
|
||||
@ -540,7 +548,7 @@ def file(name,
|
||||
source,
|
||||
source_hash,
|
||||
source_hash_name,
|
||||
owner,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
template,
|
||||
@ -565,7 +573,7 @@ def file(name,
|
||||
template,
|
||||
source,
|
||||
source_hash,
|
||||
owner,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
__env__,
|
||||
@ -593,7 +601,7 @@ def file(name,
|
||||
ret,
|
||||
source,
|
||||
source_sum,
|
||||
owner,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
__env__,
|
||||
@ -606,17 +614,22 @@ def file(name,
|
||||
return ret
|
||||
|
||||
cron_ret = None
|
||||
if ret['changes']:
|
||||
if "diff" in ret['changes']:
|
||||
cron_ret = __salt__['cron.write_cron_file_verbose'](user, cron_path)
|
||||
ret['comment'] = 'Crontab for user {0} was updated'.format(user)
|
||||
# Check cmd return code and show success or failure
|
||||
if cron_ret['retcode'] == 0:
|
||||
ret['comment'] = 'Crontab for user {0} was updated'.format(user)
|
||||
ret['result'] = True
|
||||
ret['changes'] = ret['changes']['diff']
|
||||
else:
|
||||
ret['comment'] = 'Unable to update user {0} crontab {1}.' \
|
||||
' Error: {2}'.format(user, cron_path, cron_ret['stderr'])
|
||||
ret['result'] = False
|
||||
ret['changes'] = {}
|
||||
elif ret['result']:
|
||||
ret['comment'] = 'Crontab for user {0} is in the correct ' \
|
||||
'state'.format(user)
|
||||
|
||||
if cron_ret and cron_ret['retcode']:
|
||||
ret['comment'] = 'Unable to update user {0} crontab {1}.' \
|
||||
' Error: {2}'.format(user, cron_path, cron_ret['stderr'])
|
||||
ret['result'] = False
|
||||
ret['changes'] = {}
|
||||
|
||||
os.unlink(cron_path)
|
||||
return ret
|
||||
|
@ -35,6 +35,10 @@ L = '# Lines below here are managed by Salt, do not edit\n'
|
||||
|
||||
CRONTAB = StringIO()
|
||||
|
||||
# Setup globals
|
||||
cron.__salt__ = {}
|
||||
cron.__grains__ = {}
|
||||
|
||||
|
||||
def get_crontab(*args, **kw):
|
||||
return CRONTAB.getvalue()
|
||||
@ -597,6 +601,243 @@ class CronTestCase(TestCase):
|
||||
'minute': '*',
|
||||
'month': '*'})
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cron_file_root_rh(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: RedHat
|
||||
- If instance running uid matches crontab user uid, runas STUB_USER without -u flag.
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cron_file_foo_rh(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: RedHat
|
||||
- If instance running with uid that doesn't match crontab user uid, run with -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab -u foo /tmp",
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cron_file_root_sol(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cron_file_foo_sol(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp",
|
||||
runas='foo',
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cron_file_root_aix(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cron_file_foo_aix(self):
|
||||
'''
|
||||
Assert that write_cron_file() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}):
|
||||
cron.write_cron_file('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp",
|
||||
runas='foo',
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cr_file_v_root_rh(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat
|
||||
- If instance running uid matches crontab user uid, runas STUB_USER without -u flag.
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cr_file_v_foo_rh(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat
|
||||
- If instance running with uid that doesn't match crontab user uid, run with -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab -u foo /tmp",
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cr_file_v_root_sol(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cr_file_v_foo_sol(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp",
|
||||
runas='foo',
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_write_cr_file_v_root_aix(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose(STUB_USER, STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp",
|
||||
runas=STUB_USER,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_write_cr_file_v_foo_aix(self):
|
||||
'''
|
||||
Assert that write_cron_file_verbose() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}):
|
||||
cron.write_cron_file_verbose('foo', STUB_PATH)
|
||||
cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp",
|
||||
runas='foo',
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_raw_cron_root_redhat(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: RedHat
|
||||
- If instance running uid matches crontab user uid, runas STUB_USER without -u flag.
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
|
||||
runas=STUB_USER,
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_raw_cron_foo_redhat(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: RedHat
|
||||
- If instance running with uid that doesn't match crontab user uid, run with -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'RedHat'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -u root -l",
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_raw_cron_root_solaris(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
|
||||
runas=STUB_USER,
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_raw_cron_foo_solaris(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: Solaris
|
||||
- Solaris should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'Solaris'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
|
||||
runas=STUB_USER,
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=True))
|
||||
def test_raw_cron_root_aix(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
|
||||
runas=STUB_USER,
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
@patch("salt.modules.cron._check_instance_uid_match", new=MagicMock(return_value=False))
|
||||
def test_raw_cron_foo_aix(self):
|
||||
'''
|
||||
Assert that raw_cron() is called with the correct cron command and user: AIX
|
||||
- AIX should always run without a -u flag
|
||||
'''
|
||||
cron.__grains__ = {'os_family': 'AIX'}
|
||||
with patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}):
|
||||
cron.raw_cron(STUB_USER)
|
||||
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
|
||||
runas=STUB_USER,
|
||||
rstrip=False,
|
||||
python_shell=False)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PsTestCase(TestCase):
|
||||
@ -615,6 +856,13 @@ class PsTestCase(TestCase):
|
||||
def test__get_cron_cmdstr(self):
|
||||
self.assertEqual('crontab /tmp', cron._get_cron_cmdstr(STUB_PATH))
|
||||
|
||||
# Test get_cron_cmdstr() when user is added
|
||||
def test__get_cron_cmdstr_user(self):
|
||||
'''
|
||||
Passes if a user is added to crontab command
|
||||
'''
|
||||
self.assertEqual('crontab -u root /tmp', cron._get_cron_cmdstr(STUB_PATH, STUB_USER))
|
||||
|
||||
def test__date_time_match(self):
|
||||
'''
|
||||
Passes if a match is found on all elements. Note the conversions to strings here!
|
||||
|
Loading…
Reference in New Issue
Block a user