mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
1817 lines
86 KiB
Python
1817 lines
86 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Import python libs
|
|
from __future__ import absolute_import
|
|
from datetime import datetime
|
|
import json
|
|
import pprint
|
|
import tempfile
|
|
|
|
try:
|
|
from dateutil.relativedelta import relativedelta
|
|
HAS_DATEUTIL = True
|
|
except ImportError:
|
|
HAS_DATEUTIL = False
|
|
|
|
NO_DATEUTIL_REASON = 'python-dateutil is not installed'
|
|
|
|
# Import Salt Testing libs
|
|
from salttesting import skipIf, TestCase
|
|
from salttesting.helpers import destructiveTest, ensure_in_syspath
|
|
from salttesting.mock import (
|
|
NO_MOCK,
|
|
NO_MOCK_REASON,
|
|
MagicMock,
|
|
call,
|
|
mock_open,
|
|
patch)
|
|
|
|
ensure_in_syspath('../../')
|
|
|
|
# Import third party libs
|
|
import yaml
|
|
|
|
# Import salt libs
|
|
import salt.states.file as filestate
|
|
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
|
|
import salt.utils
|
|
import os
|
|
import shutil
|
|
|
|
filestate.__env__ = 'base'
|
|
filestate.__salt__ = {'file.manage_file': False}
|
|
filestate.__serializers__ = {
|
|
'yaml.serialize': yamlserializer.serialize,
|
|
'python.serialize': pythonserializer.serialize,
|
|
'json.serialize': jsonserializer.serialize
|
|
}
|
|
filestate.__opts__ = {'test': False, 'cachedir': ''}
|
|
filestate.__instance_id__ = ''
|
|
filestate.__grains__ = {}
|
|
filestate.__low__ = {}
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
class TestFileState(TestCase):
|
|
|
|
def test_serialize(self):
|
|
def returner(contents, *args, **kwargs):
|
|
returner.returned = contents
|
|
returner.returned = None
|
|
|
|
filestate.__salt__ = {
|
|
'file.manage_file': returner
|
|
}
|
|
|
|
dataset = {
|
|
"foo": True,
|
|
"bar": 42,
|
|
"baz": [1, 2, 3],
|
|
"qux": 2.0
|
|
}
|
|
|
|
filestate.serialize('/tmp', dataset)
|
|
self.assertEqual(yaml.load(returner.returned), dataset)
|
|
|
|
filestate.serialize('/tmp', dataset, formatter="yaml")
|
|
self.assertEqual(yaml.load(returner.returned), dataset)
|
|
|
|
filestate.serialize('/tmp', dataset, formatter="json")
|
|
self.assertEqual(json.loads(returner.returned), dataset)
|
|
|
|
filestate.serialize('/tmp', dataset, formatter="python")
|
|
self.assertEqual(returner.returned, pprint.pformat(dataset) + '\n')
|
|
|
|
def test_contents_and_contents_pillar(self):
|
|
def returner(contents, *args, **kwargs):
|
|
returner.returned = contents
|
|
returner.returned = None
|
|
|
|
filestate.__salt__ = {
|
|
'file.manage_file': returner
|
|
}
|
|
|
|
manage_mode_mock = MagicMock()
|
|
filestate.__salt__['config.manage_mode'] = manage_mode_mock
|
|
|
|
ret = filestate.managed('/tmp/foo', contents='hi', contents_pillar='foo:bar')
|
|
self.assertEqual(False, ret['result'])
|
|
|
|
def test_contents_pillar_doesnt_add_more_newlines(self):
|
|
# make sure the newline
|
|
pillar_value = 'i am the pillar value\n'
|
|
|
|
self.run_contents_pillar(pillar_value, expected=pillar_value)
|
|
|
|
def run_contents_pillar(self, pillar_value, expected):
|
|
returner = MagicMock(return_value=None)
|
|
|
|
filestate.__salt__ = {
|
|
'file.manage_file': returner
|
|
}
|
|
|
|
path = '/tmp/foo'
|
|
pillar_path = 'foo:bar'
|
|
|
|
# the values don't matter here
|
|
filestate.__salt__['config.manage_mode'] = MagicMock()
|
|
filestate.__salt__['file.source_list'] = MagicMock(return_value=[None, None])
|
|
filestate.__salt__['file.get_managed'] = MagicMock(return_value=[None, None, None])
|
|
|
|
# pillar.get should return the pillar_value
|
|
pillar_mock = MagicMock(return_value=pillar_value)
|
|
filestate.__salt__['pillar.get'] = pillar_mock
|
|
|
|
ret = filestate.managed(path, contents_pillar=pillar_path)
|
|
|
|
# make sure no errors are returned
|
|
self.assertEqual(None, ret)
|
|
|
|
# Make sure the contents value matches the expected value.
|
|
# returner.call_args[0] will be an args tuple containing all the args
|
|
# passed to the mocked returner for file.manage_file. Any changes to
|
|
# the arguments for file.manage_file may make this assertion fail.
|
|
# If the test is failing, check the position of the "contents" param
|
|
# in the manage_file() function in salt/modules/file.py, the fix is
|
|
# likely as simple as updating the 2nd index below.
|
|
self.assertEqual(expected, returner.call_args[0][-5])
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
|
class FileTestCase(TestCase):
|
|
|
|
'''
|
|
Test cases for salt.states.file
|
|
'''
|
|
# 'symlink' function tests: 1
|
|
|
|
@destructiveTest
|
|
def test_symlink(self):
|
|
'''
|
|
Test to create a symlink.
|
|
'''
|
|
name = '/tmp/testfile.txt'
|
|
target = tempfile.mkstemp()[1]
|
|
test_dir = '/tmp'
|
|
user = 'salt'
|
|
|
|
if salt.utils.is_windows():
|
|
group = 'salt'
|
|
else:
|
|
group = 'saltstack'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_empty = MagicMock(return_value='')
|
|
mock_uid = MagicMock(return_value='U1001')
|
|
mock_gid = MagicMock(return_value='g1001')
|
|
mock_target = MagicMock(return_value=target)
|
|
mock_user = MagicMock(return_value=user)
|
|
mock_grp = MagicMock(return_value=group)
|
|
mock_os_error = MagicMock(side_effect=OSError)
|
|
|
|
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t}):
|
|
comt = ('Must provide name to file.symlink')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.symlink('', target), ret)
|
|
|
|
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_empty,
|
|
'file.group_to_gid': mock_empty}):
|
|
comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group))
|
|
ret.update({'comment': comt, 'name': 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,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f}):
|
|
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}})
|
|
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,
|
|
'file.is_link': mock_f}):
|
|
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,
|
|
'result': False,
|
|
'pchanges': {'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,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_t,
|
|
'file.readlink': mock_target}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target}):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target}):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target,
|
|
'file.symlink': mock_t,
|
|
'user.info': mock_t,
|
|
'file.lchown': mock_f}):
|
|
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_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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target,
|
|
'file.symlink': mock_t,
|
|
'user.info': mock_t,
|
|
'file.lchown': mock_f}):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target,
|
|
'file.symlink': mock_os_error,
|
|
'user.info': mock_t,
|
|
'file.lchown': mock_f}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target,
|
|
'file.symlink': mock_t,
|
|
'user.info': mock_t,
|
|
'file.lchown': mock_f,
|
|
'file.get_user': mock_user,
|
|
'file.get_group': mock_grp}):
|
|
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.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.is_link': mock_f,
|
|
'file.readlink': mock_target,
|
|
'file.symlink': mock_t,
|
|
'user.info': mock_t,
|
|
'file.lchown': mock_f,
|
|
'file.get_user': mock_empty,
|
|
'file.get_group': mock_empty}):
|
|
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)
|
|
|
|
# 'absent' function tests: 1
|
|
def test_absent(self):
|
|
'''
|
|
Test to make sure that the named file or directory is absent.
|
|
'''
|
|
name = '/fake/file.conf'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_file = MagicMock(side_effect=[True, CommandExecutionError])
|
|
mock_tree = MagicMock(side_effect=[True, OSError])
|
|
|
|
comt = ('Must provide name to file.absent')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
|
|
with patch.object(os.path, 'islink', MagicMock(return_value=False)):
|
|
self.assertDictEqual(filestate.absent(''), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
comt = ('Refusing to make "/" absent')
|
|
ret.update({'comment': comt, 'name': '/'})
|
|
self.assertDictEqual(filestate.absent('/'), ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_t):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File {0} is set for removal'.format(name))
|
|
ret.update({'comment': comt,
|
|
'name': name,
|
|
'result': None,
|
|
'pchanges': {'removed': '/fake/file.conf'}})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
ret.update({'pchanges': {}})
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.remove': mock_file}):
|
|
comt = ('Removed file {0}'.format(name))
|
|
ret.update({'comment': comt, 'result': True,
|
|
'changes': {'removed': name},
|
|
'pchanges': {'removed': name}})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
|
|
comt = ('Removed file {0}'.format(name))
|
|
ret.update({'comment': '',
|
|
'result': False,
|
|
'changes': {}})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
ret.update({'pchanges': {}})
|
|
|
|
with patch.object(os.path, 'isfile', mock_f):
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = \
|
|
'Directory {0} is set for removal'.format(name)
|
|
ret.update({'comment': comt,
|
|
'pchanges': {'removed': name},
|
|
'result': None})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.remove': mock_tree}):
|
|
comt = ('Removed directory {0}'.format(name))
|
|
ret.update({'comment': comt, 'result': True,
|
|
'changes': {'removed': name}})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
|
|
comt = \
|
|
'Failed to remove directory {0}'.format(name)
|
|
ret.update({'comment': comt, 'result': False,
|
|
'changes': {}})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
ret.update({'pchanges': {}})
|
|
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File {0} is not present'.format(name))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.absent(name), ret)
|
|
|
|
# 'exists' function tests: 1
|
|
|
|
def test_exists(self):
|
|
'''
|
|
Test to verify that the named file or directory is present or exists.
|
|
'''
|
|
name = '/etc/grub.conf'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {},
|
|
'pchanges': {}}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
|
|
comt = ('Must provide name to file.exists')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.exists(''), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_f):
|
|
comt = ('Specified path {0} does not exist'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.exists(name), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
comt = ('Path {0} exists'.format(name))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.exists(name), ret)
|
|
|
|
# 'missing' function tests: 1
|
|
|
|
def test_missing(self):
|
|
'''
|
|
Test to verify that the named file or directory is missing.
|
|
'''
|
|
name = '/etc/grub.conf'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
|
|
comt = ('Must provide name to file.missing')
|
|
ret.update({'comment': comt, 'name': '', 'pchanges': {}})
|
|
self.assertDictEqual(filestate.missing(''), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
comt = ('Specified path {0} exists'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.missing(name), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_f):
|
|
comt = ('Path {0} is missing'.format(name))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.missing(name), ret)
|
|
|
|
# 'managed' function tests: 1
|
|
|
|
@patch('salt.states.file._load_accumulators',
|
|
MagicMock(return_value=([], [])))
|
|
def test_managed(self):
|
|
'''
|
|
Test to manage a given file, this function allows for a file to be
|
|
downloaded from the salt master and potentially run through a templating
|
|
system.
|
|
'''
|
|
name = '/etc/grub.conf'
|
|
user = 'salt'
|
|
group = 'saltstack'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_cmd_fail = MagicMock(return_value={'retcode': 1})
|
|
mock_uid = MagicMock(side_effect=['', 'U12', 'U12', 'U12', 'U12', 'U12',
|
|
'U12', 'U12', 'U12', 'U12', 'U12',
|
|
'U12', 'U12', 'U12', 'U12', 'U12'])
|
|
mock_gid = MagicMock(side_effect=['', 'G12', 'G12', 'G12', 'G12', 'G12',
|
|
'G12', 'G12', 'G12', 'G12', 'G12',
|
|
'G12', 'G12', 'G12', 'G12', 'G12'])
|
|
mock_if = MagicMock(side_effect=[True, False, False, False, False,
|
|
False, False, False])
|
|
mock_ret = MagicMock(return_value=(ret, None))
|
|
mock_dict = MagicMock(return_value={})
|
|
mock_cp = MagicMock(side_effect=[Exception, True])
|
|
mock_ex = MagicMock(side_effect=[Exception, {'changes': {name: name}},
|
|
True, Exception])
|
|
mock_mng = MagicMock(side_effect=[Exception, ('', '', ''), ('', '', ''),
|
|
('', '', True), ('', '', True),
|
|
('', '', ''), ('', '', '')])
|
|
mock_file = MagicMock(side_effect=[CommandExecutionError, ('', ''),
|
|
('', ''), ('', ''), ('', ''),
|
|
('', ''), ('', ''), ('', ''),
|
|
('', '')])
|
|
with patch.dict(filestate.__salt__,
|
|
{'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.file_exists': mock_if,
|
|
'file.check_perms': mock_ret,
|
|
'file.check_managed_changes': mock_dict,
|
|
'file.get_managed': mock_mng,
|
|
'file.source_list': mock_file,
|
|
'file.copy': mock_cp,
|
|
'file.manage_file': mock_ex,
|
|
'cmd.run_all': mock_cmd_fail}):
|
|
comt = ('Must provide name to file.exists')
|
|
ret.update({'comment': comt, 'name': '', 'pchanges': {}})
|
|
self.assertDictEqual(filestate.managed(''), ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_f):
|
|
comt = ('File {0} is not present and is not set for '
|
|
'creation'.format(name))
|
|
ret.update({'comment': comt, 'name': name, 'result': True})
|
|
self.assertDictEqual(filestate.managed(name, create=False),
|
|
ret)
|
|
|
|
comt = ('User salt is not available Group saltstack'
|
|
' is not available')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
comt = ('Specified target {0} is a directory'.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
comt = ('Context must be formed as a dict')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed(name, user=user,
|
|
group=group,
|
|
context=True), ret)
|
|
|
|
comt = ('Defaults must be formed as a dict')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed(name, user=user,
|
|
group=group,
|
|
defaults=True), ret)
|
|
|
|
comt = ('Only one of \'contents\', \'contents_pillar\', '
|
|
'and \'contents_grains\' is permitted')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
contents='A', contents_grains='B',
|
|
contents_pillar='C'), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File {0} not updated'.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
replace=False), ret)
|
|
|
|
comt = ('The file {0} is in the correct state'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, contents='A',
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_f):
|
|
with patch.dict(filestate.__opts__,
|
|
{'test': False}):
|
|
comt = ('Unable to manage file: ')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
contents='A'), ret)
|
|
|
|
comt = ('Unable to manage file: ')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
contents='A'), ret)
|
|
|
|
with patch.object(salt.utils, 'mkstemp',
|
|
return_value=name):
|
|
comt = ('Unable to copy file {0} to {1}: '
|
|
.format(name, name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user,
|
|
group=group,
|
|
check_cmd='A'), ret)
|
|
|
|
comt = ('Unable to check_cmd file: ')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
check_cmd='A'), ret)
|
|
|
|
comt = ('check_cmd execution failed')
|
|
ret.update({'comment': comt, 'result': False, 'skip_watch': True})
|
|
ret.pop('pchanges')
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group,
|
|
check_cmd='A'), ret)
|
|
|
|
comt = ('check_cmd execution failed')
|
|
ret.update({'comment': True, 'pchanges': {}})
|
|
ret.pop('skip_watch', None)
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group),
|
|
ret)
|
|
|
|
self.assertTrue(filestate.managed
|
|
(name, user=user, group=group))
|
|
|
|
comt = ('Unable to manage file: ')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.managed
|
|
(name, user=user, group=group),
|
|
ret)
|
|
|
|
# 'directory' function tests: 1
|
|
|
|
def test_directory(self):
|
|
'''
|
|
Test to ensure that a named directory is present and has the right perms
|
|
'''
|
|
name = '/etc/grub.conf'
|
|
user = 'salt'
|
|
group = 'saltstack'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.directory')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.directory(''), ret)
|
|
|
|
comt = ('Cannot specify both max_depth and clean')
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(
|
|
filestate.directory(name, clean=True, max_depth=2),
|
|
ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_perms = MagicMock(return_value=(ret, ''))
|
|
mock_uid = MagicMock(side_effect=['', 'U12', 'U12', 'U12', 'U12', 'U12',
|
|
'U12', 'U12', 'U12', 'U12', 'U12'])
|
|
mock_gid = MagicMock(side_effect=['', 'G12', 'G12', 'G12', 'G12', 'G12',
|
|
'G12', 'G12', 'G12', 'G12', 'G12'])
|
|
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.stats': mock_f,
|
|
'file.check_perms': mock_perms,
|
|
'file.mkdir': mock_t}):
|
|
if salt.utils.is_windows():
|
|
comt = ('User salt is not available Group salt'
|
|
' is not available')
|
|
else:
|
|
comt = ('User salt is not available Group saltstack'
|
|
' is not available')
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.directory(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'
|
|
.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.directory(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'isfile',
|
|
MagicMock(side_effect=[True, True, False,
|
|
True, True, True,
|
|
False])):
|
|
with patch.object(os.path, 'lexists', mock_t):
|
|
comt = ('File exists where the backup target'
|
|
' A should go')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.directory
|
|
(name, user=user,
|
|
group=group,
|
|
backupname='A'), ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_t):
|
|
comt = ('Specified location {0} exists and is a file'
|
|
.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.directory(name, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'islink', mock_t):
|
|
comt = ('Specified location {0} exists and is a symlink'
|
|
.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.directory(name,
|
|
user=user,
|
|
group=group),
|
|
ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('The following files will be changed:\n{0}:'
|
|
' directory - new\n'.format(name))
|
|
ret.update({'comment': comt, 'result': None, 'pchanges': {'/etc/grub.conf': {'directory': 'new'}}})
|
|
self.assertDictEqual(filestate.directory(name,
|
|
user=user,
|
|
group=group),
|
|
ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
comt = ('No directory to create {0} in'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.directory
|
|
(name, user=user, group=group),
|
|
ret)
|
|
|
|
with patch.object(os.path, 'isdir',
|
|
MagicMock(side_effect=[True, False, True, True])):
|
|
comt = ('Failed to create directory {0}'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.directory
|
|
(name, user=user, group=group),
|
|
ret)
|
|
|
|
recurse = ['ignore_files', 'ignore_dirs']
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
self.assertDictEqual(filestate.directory
|
|
(name, user=user,
|
|
recurse=recurse, group=group),
|
|
ret)
|
|
|
|
self.assertDictEqual(filestate.directory
|
|
(name, user=user, group=group),
|
|
ret)
|
|
|
|
# 'recurse' function tests: 1
|
|
|
|
def test_recurse(self):
|
|
'''
|
|
Test to recurse through a subdirectory on the master
|
|
and copy said subdirectory over to the specified path.
|
|
'''
|
|
name = '/opt/code/flask'
|
|
source = 'salt://code/flask'
|
|
user = 'salt'
|
|
group = 'saltstack'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
comt = ("'mode' is not allowed in 'file.recurse'."
|
|
" Please use 'file_mode' and 'dir_mode'.")
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source, mode='W'), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_uid = MagicMock(return_value='')
|
|
mock_gid = MagicMock(return_value='')
|
|
mock_l = MagicMock(return_value=[])
|
|
mock_emt = MagicMock(side_effect=[[], ['code/flask'], ['code/flask']])
|
|
mock_lst = MagicMock(side_effect=[CommandExecutionError, (source, ''),
|
|
(source, ''), (source, '')])
|
|
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
|
|
'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.source_list': mock_lst,
|
|
'cp.list_master_dirs': mock_emt,
|
|
'cp.list_master': mock_l}):
|
|
comt = ('User salt is not available Group saltstack'
|
|
' is not available')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source, user=user,
|
|
group=group), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'
|
|
.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
comt = ("Invalid source '1' (must be a salt:// URI)")
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, 1), ret)
|
|
|
|
comt = ("Invalid source '//code/flask' (must be a salt:// URI)")
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, '//code/flask'),
|
|
ret)
|
|
|
|
comt = ('Recurse failed: ')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source), ret)
|
|
|
|
comt = ("The directory 'code/flask' does not exist"
|
|
" on the salt fileserver in saltenv 'base'")
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source), ret)
|
|
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
comt = ('The path {0} exists and is not a directory'
|
|
.format(name))
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.recurse(name, source),
|
|
ret)
|
|
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
comt = ('The directory {0} is in the correct state'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.recurse(name, source), ret)
|
|
|
|
# 'replace' function tests: 1
|
|
|
|
def test_replace(self):
|
|
'''
|
|
Test to maintain an edit in a file.
|
|
'''
|
|
name = '/etc/grub.conf'
|
|
pattern = ('CentOS +')
|
|
repl = 'salt'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.replace')
|
|
ret.update({'comment': comt, 'name': '', 'pchanges': {}})
|
|
self.assertDictEqual(filestate.replace('', pattern, repl), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.replace(name, pattern, repl), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
with patch.dict(filestate.__salt__, {'file.replace': mock_f}):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
comt = ('No changes needed to be made')
|
|
ret.update({'comment': comt, 'name': name,
|
|
'result': True})
|
|
self.assertDictEqual(filestate.replace(name, pattern,
|
|
repl), ret)
|
|
|
|
# 'blockreplace' function tests: 1
|
|
|
|
@patch('salt.states.file._load_accumulators',
|
|
MagicMock(return_value=([], [])))
|
|
def test_blockreplace(self):
|
|
'''
|
|
Test to maintain an edit in a file in a zone
|
|
delimited by two line markers.
|
|
'''
|
|
name = '/etc/hosts'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.blockreplace')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.blockreplace(''), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.blockreplace(name), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.dict(filestate.__salt__, {'file.blockreplace': mock_t}):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('Changes would be made')
|
|
ret.update({'comment': comt, 'result': None,
|
|
'changes': {'diff': True},
|
|
'pchanges': {'diff': True}})
|
|
self.assertDictEqual(filestate.blockreplace(name), ret)
|
|
|
|
# 'comment' function tests: 1
|
|
|
|
@destructiveTest
|
|
@patch.object(os.path, 'exists', MagicMock(return_value=True))
|
|
def test_comment(self):
|
|
'''
|
|
Test to comment out specified lines in a file.
|
|
'''
|
|
name = '/etc/aliases' if salt.utils.is_darwin() else '/etc/fstab'
|
|
regex = 'bind 127.0.0.1'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.comment')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.comment('', regex), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.comment(name, regex), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.search': MagicMock(side_effect=[True, True, True, False, False])}):
|
|
comt = ('Pattern already commented')
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.comment(name, regex), ret)
|
|
|
|
comt = ('{0}: Pattern not found'.format(regex))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.comment(name, regex), ret)
|
|
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.search': MagicMock(side_effect=[False, True, False, True, True]),
|
|
'file.comment': mock_t,
|
|
'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': {name: 'updated'}})
|
|
self.assertDictEqual(filestate.comment(name, regex), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(salt.utils, 'fopen',
|
|
MagicMock(mock_open())):
|
|
comt = ('Commented lines successfully')
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.comment(name, regex),
|
|
ret)
|
|
|
|
# 'uncomment' function tests: 1
|
|
|
|
@destructiveTest
|
|
@patch.object(os.path, 'exists', MagicMock(return_value=True))
|
|
def test_uncomment(self):
|
|
'''
|
|
Test to uncomment specified commented lines in a file
|
|
'''
|
|
name = '/etc/aliases' if salt.utils.is_darwin() else '/etc/fstab'
|
|
regex = 'bind 127.0.0.1'
|
|
|
|
ret = {'name': name,
|
|
'pchanges': {},
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.uncomment')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.uncomment('', regex), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock = MagicMock(side_effect=[True, False, False, False, True, False,
|
|
True, True])
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.search': mock,
|
|
'file.uncomment': mock_t,
|
|
'file.comment_line': mock_t}):
|
|
comt = ('Pattern already uncommented')
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
|
|
|
comt = ('{0}: Pattern not found'.format(regex))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File {0} is set to be updated'.format(name))
|
|
ret.update({'comment': comt, 'result': None, 'pchanges': {name: 'updated'}, })
|
|
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(salt.utils, 'fopen',
|
|
MagicMock(mock_open())):
|
|
comt = ('Uncommented lines successfully')
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
|
|
|
# 'prepend' function tests: 1
|
|
|
|
def test_prepend(self):
|
|
'''
|
|
Test to ensure that some text appears at the beginning of a file.
|
|
'''
|
|
name = '/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.']
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'pchanges': {},
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.prepend')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.prepend(''), ret)
|
|
|
|
comt = ('source and sources are mutually exclusive')
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.prepend(name, source=source,
|
|
sources=sources), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.directory_exists': mock_f,
|
|
'file.makedirs': mock_t,
|
|
'file.stats': mock_f,
|
|
'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)
|
|
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'pchanges': {}})
|
|
self.assertDictEqual(filestate.prepend(name), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
comt = ("Failed to load template file {0}".format(source))
|
|
ret.pop('pchanges')
|
|
ret.update({'comment': comt, 'name': source, 'data': []})
|
|
self.assertDictEqual(filestate.prepend(name, source=source),
|
|
ret)
|
|
|
|
ret.pop('data', None)
|
|
ret.update({'name': name})
|
|
with patch.object(salt.utils, 'fopen',
|
|
MagicMock(mock_open(read_data=''))):
|
|
with patch.object(salt.utils, 'istextfile', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
change = {'diff': 'Replace binary file'}
|
|
comt = ('File {0} is set to be updated'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': None,
|
|
'changes': change, 'pchanges': {}})
|
|
self.assertDictEqual(filestate.prepend
|
|
(name, text=text), ret)
|
|
|
|
with patch.dict(filestate.__opts__,
|
|
{'test': False}):
|
|
comt = ('Prepended 1 lines')
|
|
ret.update({'comment': comt, 'result': True,
|
|
'changes': {}})
|
|
self.assertDictEqual(filestate.prepend
|
|
(name, text=text), ret)
|
|
|
|
# 'patch' function tests: 1
|
|
|
|
def test_patch(self):
|
|
'''
|
|
Test to apply a patch to a file.
|
|
'''
|
|
name = '/opt/file.txt'
|
|
source = 'salt://file.patch'
|
|
ha_sh = 'md5=e138491e9d5b97023cea823fe17bac22'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.patch')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.patch(''), ret)
|
|
|
|
comt = ('{0}: file not found'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.patch(name), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_true = MagicMock(side_effect=[True, False, False, False, False])
|
|
mock_false = MagicMock(side_effect=[False, True, True, True])
|
|
mock_ret = MagicMock(return_value={'retcode': True})
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
comt = ('Source is required')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.patch(name), ret)
|
|
|
|
comt = ('Hash is required')
|
|
ret.update({'comment': comt})
|
|
self.assertDictEqual(filestate.patch(name, source=source), ret)
|
|
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.check_hash': mock_true,
|
|
'cp.cache_file': mock_false,
|
|
'file.patch': mock_ret}):
|
|
comt = ('Patch is already applied')
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.patch(name, source=source,
|
|
hash=ha_sh), ret)
|
|
|
|
comt = ("Unable to cache salt://file.patch"
|
|
" from saltenv 'base'")
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.patch(name, source=source,
|
|
hash=ha_sh), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File /opt/file.txt will be patched')
|
|
ret.update({'comment': comt, 'result': None,
|
|
'changes': {'retcode': True}})
|
|
self.assertDictEqual(filestate.patch(name,
|
|
source=source,
|
|
hash=ha_sh), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
ret.update({'comment': '', 'result': False})
|
|
self.assertDictEqual(filestate.patch(name,
|
|
source=source,
|
|
hash=ha_sh), ret)
|
|
|
|
self.assertDictEqual(filestate.patch
|
|
(name, source=source, hash=ha_sh,
|
|
dry_run_first=False), ret)
|
|
|
|
# 'touch' function tests: 1
|
|
|
|
def test_touch(self):
|
|
'''
|
|
Test to replicate the 'nix "touch" command to create a new empty
|
|
file or update the atime and mtime of an existing file.
|
|
'''
|
|
name = '/var/log/httpd/logrotate.empty'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.touch')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.touch(''), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.touch(name), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'exists', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File {0} is set to be created'.format(name))
|
|
ret.update({'comment': comt, 'result': None})
|
|
self.assertDictEqual(filestate.touch(name), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
comt = ('Directory not present to touch file {0}'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.touch(name), ret)
|
|
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
with patch.dict(filestate.__salt__, {'file.touch': mock_t}):
|
|
comt = ('Created empty file {0}'.format(name))
|
|
ret.update({'comment': comt, 'result': True,
|
|
'changes': {'new': name}})
|
|
self.assertDictEqual(filestate.touch(name), ret)
|
|
|
|
# 'copy' function tests: 1
|
|
|
|
def test_copy(self):
|
|
'''
|
|
Test if the source file exists on the system, copy it to the named file.
|
|
'''
|
|
name = '/tmp/salt'
|
|
source = '/tmp/salt/salt'
|
|
user = 'salt'
|
|
group = 'saltstack'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.copy')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.copy('', source), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_uid = MagicMock(side_effect=[''])
|
|
mock_gid = MagicMock(side_effect=[''])
|
|
mock_user = MagicMock(return_value=user)
|
|
mock_grp = MagicMock(return_value=group)
|
|
mock_io = MagicMock(side_effect=IOError)
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.copy(name, source), ret)
|
|
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'exists', mock_f):
|
|
comt = ('Source file "{0}" is not present'.format(source))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.copy(name, source), ret)
|
|
|
|
with patch.object(os.path, 'exists', mock_t):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.user_to_uid': mock_uid,
|
|
'file.group_to_gid': mock_gid,
|
|
'file.get_user': mock_user,
|
|
'file.get_group': mock_grp,
|
|
'file.get_mode': mock_grp,
|
|
'file.check_perms': mock_t}):
|
|
comt = ('User salt is not available Group '
|
|
'saltstack is not available')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.copy(name, source, user=user,
|
|
group=group), ret)
|
|
|
|
comt1 = ('Failed to delete "{0}" in preparation for'
|
|
' forced move'.format(name))
|
|
comt2 = ('The target file "{0}" exists and will not be '
|
|
'overwritten'.format(name))
|
|
comt3 = ('File "{0}" is set to be copied to "{1}"'
|
|
.format(source, name))
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
with patch.object(os.path, 'lexists', mock_t):
|
|
with patch.dict(filestate.__opts__,
|
|
{'test': False}):
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.remove': mock_io}):
|
|
ret.update({'comment': comt1,
|
|
'result': False})
|
|
self.assertDictEqual(filestate.copy
|
|
(name, source,
|
|
preserve=True,
|
|
force=True), ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_t):
|
|
ret.update({'comment': comt2,
|
|
'result': True})
|
|
self.assertDictEqual(filestate.copy
|
|
(name, source,
|
|
preserve=True), ret)
|
|
|
|
with patch.object(os.path, 'lexists', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
ret.update({'comment': comt3, 'result': None})
|
|
self.assertDictEqual(filestate.copy
|
|
(name, source,
|
|
preserve=True), ret)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
comt = ('The target directory /tmp is'
|
|
' not present')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.copy
|
|
(name, source,
|
|
preserve=True), ret)
|
|
|
|
# 'rename' function tests: 1
|
|
|
|
def test_rename(self):
|
|
'''
|
|
Test if the source file exists on the system,
|
|
rename it to the named file.
|
|
'''
|
|
name = '/tmp/salt'
|
|
source = '/tmp/salt/salt'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.rename')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.rename('', source), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
|
|
mock_lex = MagicMock(side_effect=[False, True, True])
|
|
with patch.object(os.path, 'isabs', mock_f):
|
|
comt = ('Specified file {0} is not an absolute path'.format(name))
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.rename(name, source), ret)
|
|
|
|
mock_lex = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
comt = ('Source file "{0}" has already been moved out of '
|
|
'place'.format(source))
|
|
ret.update({'comment': comt, 'result': True})
|
|
self.assertDictEqual(filestate.rename(name, source), ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, True, True])
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
comt = ('The target file "{0}" exists and will not be '
|
|
'overwritten'.format(name))
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.rename(name, source), ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, True, True])
|
|
mock_rem = MagicMock(side_effect=IOError)
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
comt = ('Failed to delete "{0}" in preparation for '
|
|
'forced move'.format(name))
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.remove': mock_rem}):
|
|
ret.update({'name': name,
|
|
'comment': comt,
|
|
'result': False})
|
|
self.assertDictEqual(filestate.rename(name, source,
|
|
force=True), ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, False, False])
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('File "{0}" is set to be moved to "{1}"'
|
|
.format(source, name))
|
|
ret.update({'name': name,
|
|
'comment': comt,
|
|
'result': None})
|
|
self.assertDictEqual(filestate.rename(name, source), ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, False, False])
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
with patch.object(os.path, 'isdir', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
comt = ('The target directory /tmp is not present')
|
|
ret.update({'name': name,
|
|
'comment': comt,
|
|
'result': False})
|
|
self.assertDictEqual(filestate.rename(name, source),
|
|
ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, False, False])
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
with patch.object(os.path, 'islink', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(shutil, 'move',
|
|
MagicMock(side_effect=IOError)):
|
|
comt = ('Failed to move "{0}" to "{1}"'
|
|
.format(source, name))
|
|
ret.update({'name': name,
|
|
'comment': comt,
|
|
'result': False})
|
|
self.assertDictEqual(filestate.rename(name,
|
|
source),
|
|
ret)
|
|
|
|
mock_lex = MagicMock(side_effect=[True, False, False])
|
|
with patch.object(os.path, 'isabs', mock_t):
|
|
with patch.object(os.path, 'lexists', mock_lex):
|
|
with patch.object(os.path, 'isdir', mock_t):
|
|
with patch.object(os.path, 'islink', mock_f):
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.object(shutil, 'move', MagicMock()):
|
|
comt = ('Moved "{0}" to "{1}"'.format(source,
|
|
name))
|
|
ret.update({'name': name,
|
|
'comment': comt,
|
|
'result': True,
|
|
'changes': {name: source}})
|
|
self.assertDictEqual(filestate.rename(name,
|
|
source),
|
|
ret)
|
|
|
|
# 'accumulated' function tests: 1
|
|
|
|
@patch('salt.states.file._load_accumulators',
|
|
MagicMock(return_value=({}, {})))
|
|
@patch('salt.states.file._persist_accummulators',
|
|
MagicMock(return_value=True))
|
|
def test_accumulated(self):
|
|
'''
|
|
Test to prepare accumulator which can be used in template in file.
|
|
'''
|
|
name = 'animals_doing_things'
|
|
filename = '/tmp/animal_file.txt'
|
|
text = ' jumps over the lazy dog.'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.accumulated')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.accumulated('', filename, text), ret)
|
|
|
|
comt = ('No text supplied for accumulator')
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.accumulated(name, filename, None), ret)
|
|
|
|
with patch.dict(filestate.__low__, {'require_in': 'file',
|
|
'watch_in': 'salt',
|
|
'__sls__': 'SLS', '__id__': 'ID'}):
|
|
comt = ('Orphaned accumulator animals_doing_things in SLS:ID')
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.accumulated(name, filename, text),
|
|
ret)
|
|
|
|
with patch.dict(filestate.__low__, {'require_in': [{'file': 'A'}],
|
|
'watch_in': [{'B': 'C'}],
|
|
'__sls__': 'SLS', '__id__': 'ID'}):
|
|
comt = ('Accumulator {0} for file {1} '
|
|
'was charged by text'.format(name, filename))
|
|
ret.update({'comment': comt, 'name': name, 'result': True})
|
|
self.assertDictEqual(filestate.accumulated(name, filename, text),
|
|
ret)
|
|
|
|
# 'serialize' function tests: 1
|
|
|
|
def test_serialize(self):
|
|
'''
|
|
Test to serializes dataset and store it into managed file.
|
|
'''
|
|
name = '/etc/dummy/package.json'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.serialize')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.serialize(''), ret)
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
with patch.object(os.path, 'isfile', mock_f):
|
|
comt = ('File {0} is not present and is not set for '
|
|
'creation'.format(name))
|
|
ret.update({'comment': comt, 'name': name, 'result': True})
|
|
self.assertDictEqual(filestate.serialize(name, create=False), ret)
|
|
|
|
comt = ("Only one of 'dataset' and 'dataset_pillar' is permitted")
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.serialize(name, dataset=True,
|
|
dataset_pillar=True), ret)
|
|
|
|
comt = ("Neither 'dataset' nor 'dataset_pillar' was defined")
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.serialize(name), ret)
|
|
|
|
with patch.object(os.path, 'isfile', mock_t):
|
|
comt = ('Python format is not supported for merging')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.serialize(name, dataset=True,
|
|
merge_if_exists=True,
|
|
formatter='python'), ret)
|
|
|
|
comt = ('A format is not supported')
|
|
ret.update({'comment': comt, 'result': False})
|
|
self.assertDictEqual(filestate.serialize(name, dataset=True,
|
|
formatter='A'), ret)
|
|
mock_changes = MagicMock(return_value=True)
|
|
mock_no_changes = MagicMock(return_value=False)
|
|
|
|
# __opts__['test']=True with changes
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.check_managed_changes': mock_changes}):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('Dataset will be serialized and stored into {0}'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': None, 'changes': True})
|
|
self.assertDictEqual(
|
|
filestate.serialize(name, dataset=True,
|
|
formatter='python'), ret)
|
|
|
|
# __opts__['test']=True without changes
|
|
with patch.dict(filestate.__salt__,
|
|
{'file.check_managed_changes': mock_no_changes}):
|
|
with patch.dict(filestate.__opts__, {'test': True}):
|
|
comt = ('The file {0} is in the correct state'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': True, 'changes': False})
|
|
self.assertDictEqual(
|
|
filestate.serialize(name,
|
|
dataset=True, formatter='python'), ret)
|
|
|
|
mock = MagicMock(return_value=ret)
|
|
with patch.dict(filestate.__opts__, {'test': False}):
|
|
with patch.dict(filestate.__salt__, {'file.manage_file': mock}):
|
|
comt = ('Dataset will be serialized and stored into {0}'
|
|
.format(name))
|
|
ret.update({'comment': comt, 'result': None})
|
|
self.assertDictEqual(filestate.serialize(name, dataset=True,
|
|
formatter='python'),
|
|
ret)
|
|
|
|
# 'mknod' function tests: 1
|
|
|
|
def test_mknod(self):
|
|
'''
|
|
Test to create a special file similar to the 'nix mknod command.
|
|
'''
|
|
name = '/dev/AA'
|
|
ntype = 'a'
|
|
|
|
ret = {'name': name,
|
|
'result': False,
|
|
'comment': '',
|
|
'changes': {}}
|
|
|
|
comt = ('Must provide name to file.mknod')
|
|
ret.update({'comment': comt, 'name': ''})
|
|
self.assertDictEqual(filestate.mknod('', ntype), ret)
|
|
|
|
comt = ("Node type unavailable: 'a'. Available node types are "
|
|
"character ('c'), block ('b'), and pipe ('p')")
|
|
ret.update({'comment': comt, 'name': name})
|
|
self.assertDictEqual(filestate.mknod(name, ntype), ret)
|
|
|
|
# 'mod_run_check_cmd' function tests: 1
|
|
|
|
def test_mod_run_check_cmd(self):
|
|
'''
|
|
Test to execute the check_cmd logic.
|
|
'''
|
|
cmd = 'A'
|
|
filename = 'B'
|
|
|
|
ret = {'comment': 'check_cmd execution failed',
|
|
'result': False, 'skip_watch': True}
|
|
|
|
mock = MagicMock(side_effect=[{'retcode': 1}, {'retcode': 0}])
|
|
with patch.dict(filestate.__salt__, {'cmd.run_all': mock}):
|
|
self.assertDictEqual(filestate.mod_run_check_cmd(cmd, filename),
|
|
ret)
|
|
|
|
self.assertTrue(filestate.mod_run_check_cmd(cmd, filename))
|
|
|
|
@skipIf(not HAS_DATEUTIL, NO_DATEUTIL_REASON)
|
|
def test_retention_schedule(self):
|
|
'''
|
|
Test to execute the retention_schedule logic.
|
|
|
|
This test takes advantage of knowing which files it is generating,
|
|
which means it can easily generate list of which files it should keep.
|
|
'''
|
|
|
|
def generate_fake_files(format='example_name_%Y%m%dT%H%M%S.tar.bz2',
|
|
starting=datetime(2016, 2, 8, 9),
|
|
every=relativedelta(minutes=30),
|
|
ending=datetime(2015, 12, 25),
|
|
maxfiles=None):
|
|
'''
|
|
For starting, make sure that it's over a week from the beginning of the month
|
|
For every, pick only one of minutes, hours, days, weeks, months or years
|
|
For ending, the further away it is from starting, the slower the tests run
|
|
Full coverage requires over a year of separation, but that's painfully slow.
|
|
'''
|
|
|
|
if every.years:
|
|
ts = datetime(starting.year, 1, 1)
|
|
elif every.months:
|
|
ts = datetime(starting.year, starting.month, 1)
|
|
elif every.days:
|
|
ts = datetime(starting.year, starting.month, starting.day)
|
|
elif every.hours:
|
|
ts = datetime(starting.year, starting.month, starting.day, starting.hour)
|
|
elif every.minutes:
|
|
ts = datetime(starting.year, starting.month, starting.day, starting.hour, 0)
|
|
else:
|
|
raise NotImplementedError("not sure what you're trying to do here")
|
|
|
|
fake_files = []
|
|
count = 0
|
|
while ending < ts:
|
|
fake_files.append(ts.strftime(format=format))
|
|
count += 1
|
|
if maxfiles and count >= maxfiles:
|
|
break
|
|
ts -= every
|
|
return fake_files
|
|
|
|
fake_name = '/some/dir/name'
|
|
fake_retain = {
|
|
'most_recent': 2,
|
|
'first_of_hour': 4,
|
|
'first_of_day': 7,
|
|
'first_of_week': 6,
|
|
'first_of_month': 6,
|
|
'first_of_year': 'all',
|
|
}
|
|
fake_strptime_format = 'example_name_%Y%m%dT%H%M%S.tar.bz2'
|
|
fake_matching_file_list = generate_fake_files()
|
|
# Add some files which do not match fake_strptime_format
|
|
fake_no_match_file_list = generate_fake_files(format='no_match_%Y%m%dT%H%M%S.tar.bz2',
|
|
every=relativedelta(days=1))
|
|
|
|
def lstat_side_effect(path):
|
|
import re
|
|
from time import mktime
|
|
x = re.match(r'^[^\d]*(\d{8}T\d{6})\.tar\.bz2$', path).group(1)
|
|
ts = mktime(datetime.strptime(x, '%Y%m%dT%H%M%S').timetuple())
|
|
return {'st_atime': 0.0, 'st_ctime': 0.0, 'st_gid': 0,
|
|
'st_mode': 33188, 'st_mtime': ts,
|
|
'st_nlink': 1, 'st_size': 0, 'st_uid': 0,
|
|
}
|
|
|
|
mock_t = MagicMock(return_value=True)
|
|
mock_f = MagicMock(return_value=False)
|
|
mock_lstat = MagicMock(side_effect=lstat_side_effect)
|
|
mock_remove = MagicMock()
|
|
|
|
def run_checks(isdir=mock_t, strptime_format=None, test=False):
|
|
expected_ret = {
|
|
'name': fake_name,
|
|
'changes': {'retained': [], 'deleted': [], 'ignored': []},
|
|
'pchanges': {'retained': [], 'deleted': [], 'ignored': []},
|
|
'result': True,
|
|
'comment': 'Name provided to file.retention must be a directory',
|
|
}
|
|
if strptime_format:
|
|
fake_file_list = sorted(fake_matching_file_list + fake_no_match_file_list)
|
|
else:
|
|
fake_file_list = sorted(fake_matching_file_list)
|
|
mock_readdir = MagicMock(return_value=fake_file_list)
|
|
|
|
with patch.dict(filestate.__opts__, {'test': test}):
|
|
with patch.object(os.path, 'isdir', isdir):
|
|
mock_readdir.reset_mock()
|
|
with patch.dict(filestate.__salt__, {'file.readdir': mock_readdir}):
|
|
with patch.dict(filestate.__salt__, {'file.lstat': mock_lstat}):
|
|
mock_remove.reset_mock()
|
|
with patch.dict(filestate.__salt__, {'file.remove': mock_remove}):
|
|
if strptime_format:
|
|
actual_ret = filestate.retention_schedule(fake_name, fake_retain,
|
|
strptime_format=fake_strptime_format)
|
|
else:
|
|
actual_ret = filestate.retention_schedule(fake_name, fake_retain)
|
|
|
|
if not isdir():
|
|
mock_readdir.assert_has_calls([])
|
|
expected_ret['result'] = False
|
|
else:
|
|
mock_readdir.assert_called_once_with(fake_name)
|
|
ignored_files = fake_no_match_file_list if strptime_format else []
|
|
retained_files = set(generate_fake_files(maxfiles=fake_retain['most_recent']))
|
|
junk_list = [('first_of_hour', relativedelta(hours=1)),
|
|
('first_of_day', relativedelta(days=1)),
|
|
('first_of_week', relativedelta(weeks=1)),
|
|
('first_of_month', relativedelta(months=1)),
|
|
('first_of_year', relativedelta(years=1))]
|
|
for retainable, retain_interval in junk_list:
|
|
new_retains = set(generate_fake_files(maxfiles=fake_retain[retainable], every=retain_interval))
|
|
# if we generate less than the number of files expected,
|
|
# then the oldest file will also be retained
|
|
# (correctly, since it's the first in it's category)
|
|
if len(new_retains) < fake_retain[retainable]:
|
|
new_retains.add(fake_file_list[0])
|
|
retained_files |= new_retains
|
|
|
|
deleted_files = sorted(list(set(fake_file_list) - retained_files - set(ignored_files)), reverse=True)
|
|
retained_files = sorted(list(retained_files), reverse=True)
|
|
changes = {'retained': retained_files, 'deleted': deleted_files, 'ignored': ignored_files}
|
|
expected_ret['pchanges'] = changes
|
|
if test:
|
|
expected_ret['result'] = None
|
|
expected_ret['comment'] = ('{0} backups would have been removed from {1}.\n'
|
|
''.format(len(deleted_files), fake_name))
|
|
else:
|
|
expected_ret['comment'] = ('{0} backups were removed from {1}.\n'
|
|
''.format(len(deleted_files), fake_name))
|
|
expected_ret['changes'] = changes
|
|
mock_remove.assert_has_calls(
|
|
[call(os.path.join(fake_name, x)) for x in deleted_files],
|
|
any_order=True
|
|
)
|
|
|
|
self.assertDictEqual(actual_ret, expected_ret)
|
|
|
|
run_checks(isdir=mock_f)
|
|
run_checks()
|
|
run_checks(test=True)
|
|
run_checks(strptime_format=fake_strptime_format)
|
|
run_checks(strptime_format=fake_strptime_format, test=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
from integration import run_tests
|
|
run_tests(FileTestCase, needs_daemon=False)
|
|
run_tests(TestFileState, needs_daemon=False)
|