Merge remote-tracking branch 'upstream/2015.5' into merge-forward-2015.8

Conflicts:
    doc/ref/configuration/minion.rst
    salt/config.py
    salt/modules/sudo.py
    tests/integration/states/file.py
This commit is contained in:
Colton Myers 2015-07-02 09:46:36 -06:00
commit 24d51fb8f1
21 changed files with 2711 additions and 83 deletions

View file

@ -48,6 +48,12 @@
# The user to run salt.
#user: root
# Setting sudo_user will cause salt to run all execution modules under an sudo
# to the user given in sudo_user. The user under which the salt minion process
# itself runs will still be that provided in the user config above, but all
# execution modules run by the minion will be rerouted through sudo.
#sudo_user: saltdev
# Specify the location of the daemon process ID file.
#pidfile: /var/run/salt-minion.pid
@ -626,7 +632,7 @@
#tcp_keepalive_intvl: -1
###### Windows Software settings ######
###### Windows Software settings ######
############################################
# Location of the repository cache file on the master:
#win_repo_cachefile: 'salt://win/repo/winrepo.p'

View file

@ -195,6 +195,21 @@ need to be changed to the ownership of the new user.
sudo_user: root
.. conf_minion:: sudo_user
``sudo_user``
--------
Default: ``''``
Setting ``sudo_user`` will cause salt to run all execution modules under an
sudo to the user given in ``sudo_user``. The user under which the salt minion
process itself runs will still be that provided in :conf_minion:`user` above,
but all execution modules run by the minion will be rerouted through sudo.
.. code-block:: yaml
sudo_user: saltadm
.. conf_minion:: pidfile

View file

@ -20,6 +20,14 @@ minion exe>` should match the contents of the corresponding md5 file.
.. admonition:: Download here
* 2015.5.2
* `Salt-Minion-2015.5.2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-AMD64-Setup.exe.md5>`__
* 2015.5.1-3
* `Salt-Minion-2015.5.1-3-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.1-3-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-AMD64-Setup.exe.md5>`__
* 2015.5.0-2
* `Salt-Minion-2015.5.0-2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.0-2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-AMD64-Setup.exe.md5>`__

File diff suppressed because it is too large Load diff

View file

@ -162,8 +162,11 @@ class Batch(object):
continue
if self.opts.get('raw'):
parts.update({part['id']: part})
minion_tracker[queue]['minions'].remove(part['id'])
else:
parts.update(part)
for id in part.keys():
minion_tracker[queue]['minions'].remove(id)
except StopIteration:
# if a iterator is done:
# - set it to inactive

View file

@ -684,6 +684,8 @@ VALID_OPTS = {
# Used by salt-api for master requests timeout
'rest_timeout': int,
'sudo_user': str,
}
# default configurations
@ -846,6 +848,7 @@ DEFAULT_MINION_OPTS = {
'zmq_monitor': False,
'cache_sreqs': True,
'cmd_safe': True,
'sudo_user': '',
}
DEFAULT_MASTER_OPTS = {

View file

@ -1579,6 +1579,9 @@ def build_interface(iface, iface_type, enabled, **settings):
if iface_type not in _IFACE_TYPES:
_raise_error_iface(iface, iface_type, _IFACE_TYPES)
if 'proto' not in settings:
settings['proto'] = 'static'
if iface_type == 'slave':
settings['slave'] = 'yes'
if 'master' not in settings:

View file

@ -604,10 +604,13 @@ def get_sensor_data(**kwargs):
salt-call ipmi.get_sensor_data api_host=127.0.0.1 api_user=admin api_pass=pass
'''
import ast
with _IpmiCommand(**kwargs) as s:
data = {}
for reading in s.get_sensor_data():
data[reading['name']] = reading
if reading:
r = ast.literal_eval(repr(reading))
data[r.pop('name')] = r
return data

View file

@ -66,7 +66,7 @@ def _dscl(cmd, ctype='create'):
def _first_avail_uid():
uids = set(x.pw_uid for x in pwd.getpwall())
for idx in range(501, 2 ** 32):
for idx in range(501, 2 ** 24):
if idx not in uids:
return idx

View file

@ -1,13 +1,43 @@
# -*- coding: utf-8 -*-
'''
Allow for the calling of execution modules via sudo
Allow for the calling of execution modules via sudo.
This module is invoked by the minion if the ``sudo_user`` minion config is
present.
Example minion config:
.. code-block:: yaml
sudo_user: saltdev
Once this setting is made, any execution module call done by the minion will be
run under ``sudo -u <sudo_user> salt-call``. For example, with the above
minion config,
.. code-block:: bash
salt sudo_minion cmd.run 'cat /etc/sudoers'
is equivalent to
.. code-block:: bash
sudo -u saltdev salt-call cmd.run 'cat /etc/sudoers'
being run on ``sudo_minion``.
'''
# Import python libs
from __future__ import absolute_import
import json
try:
from shlex import quote as _cmd_quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _cmd_quote
# Import salt libs
from salt.exceptions import SaltInvocationError
import salt.utils
import salt.syspaths
@ -15,7 +45,7 @@ __virtualname__ = 'sudo'
def __virtual__():
if salt.utils.which('sudo'):
if salt.utils.which('sudo') and __opts__.get('sudo_user'):
return __virtualname__
return False
@ -24,23 +54,45 @@ def salt_call(runas, fun, *args, **kwargs):
'''
Wrap a shell execution out to salt call with sudo
CLI Example::
Example:
salt '*' sudo.salt_call root test.ping
/etc/salt/minion
.. code-block:: yaml
sudo_user: saltdev
.. code-block:: bash
salt '*' test.ping # is run as saltdev user
'''
if fun == 'sudo.salt_call':
__context__['retcode'] = 1
raise SaltInvocationError('sudo.salt_call is not designed to be run '
'directly, but is used by the minion when '
'the sudo_user config is set.')
cmd = ['sudo',
'-u', runas,
'salt-call',
'--out', 'json',
'--metadata',
'-c', salt.syspaths.CONFIG_DIR,
'--',
fun]
for arg in args:
cmd.append(arg)
cmd.append(_cmd_quote(arg))
for key in kwargs:
cmd.append('{0}={1}'.format(key, kwargs[key]))
cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False)
cmd_meta = json.loads(cmd_ret['stdout'])['local']
ret = cmd_meta['return']
__context__['retcode'] = cmd_meta.get('retcode', 0)
cmd.append(_cmd_quote('{0}={1}'.format(key, kwargs[key])))
cmd_ret = __salt__['cmd.run_all'](cmd, use_vt=True, python_shell=False)
if cmd_ret['retcode'] == 0:
cmd_meta = json.loads(cmd_ret['stdout'])['local']
ret = cmd_meta['return']
__context__['retcode'] = cmd_meta.get('retcode', 0)
else:
ret = cmd_ret['stderr']
__context__['retcode'] = cmd_ret['retcode']
return ret

View file

@ -697,7 +697,7 @@ def _validate_str_list(arg):
elif isinstance(arg, Iterable) and not isinstance(arg, Mapping):
return [str(item) for item in arg]
else:
return False
return [str(arg)]
def symlink(
@ -2066,8 +2066,6 @@ def recurse(name,
# expand source into source_list
source_list = _validate_str_list(source)
if not source_list:
return _error(ret, '\'source\' parameter is not a string or list of strings')
for idx, val in enumerate(source_list):
source_list[idx] = val.rstrip('/')
@ -3181,8 +3179,6 @@ def append(name,
text = tmpret['data']
text = _validate_str_list(text)
if not text:
return _error(ret, 'Given text is not a string or a list of strings')
with salt.utils.fopen(name, 'rb') as fp_:
slines = fp_.readlines()
@ -3350,8 +3346,6 @@ def prepend(name,
text = tmpret['data']
text = _validate_str_list(text)
if not text:
return _error(ret, 'Given text is not a string or a list of strings')
with salt.utils.fopen(name, 'rb') as fp_:
slines = fp_.readlines()

View file

@ -635,29 +635,29 @@ class Finder(object):
depth = dirpath[len(path) + len(os.path.sep):].count(os.path.sep)
if depth == self.maxdepth:
dirs[:] = []
else:
if depth >= self.mindepth:
for name in dirs + files:
fstat = None
matches = True
fullpath = None
for criterion in self.criteria:
if fstat is None and criterion.requires() & _REQUIRES_STAT:
fullpath = os.path.join(dirpath, name)
fstat = os.stat(fullpath)
if not criterion.match(dirpath, name, fstat):
matches = False
break
if matches:
if fullpath is None:
fullpath = os.path.join(dirpath, name)
for action in self.actions:
if (fstat is None and
if depth >= self.mindepth:
for name in dirs + files:
fstat = None
matches = True
fullpath = None
for criterion in self.criteria:
if fstat is None and criterion.requires() & _REQUIRES_STAT:
fullpath = os.path.join(dirpath, name)
fstat = os.stat(fullpath)
if not criterion.match(dirpath, name, fstat):
matches = False
break
if matches:
if fullpath is None:
fullpath = os.path.join(dirpath, name)
for action in self.actions:
if (fstat is None and
action.requires() & _REQUIRES_STAT):
fstat = os.stat(fullpath)
result = action.execute(fullpath, fstat, test=self.test)
if result is not None:
yield result
fstat = os.stat(fullpath)
result = action.execute(fullpath, fstat, test=self.test)
if result is not None:
yield result
def find(path, options):

View file

@ -13,6 +13,7 @@ import pwd
import shutil
import stat
import tempfile
import textwrap
import filecmp
import textwrap
@ -26,8 +27,8 @@ from salttesting.helpers import (
ensure_in_syspath,
with_system_user_and_group
)
ensure_in_syspath('../../')
ensure_in_syspath('../../')
# Import salt libs
import integration
@ -36,7 +37,6 @@ import salt.utils
# Import 3rd-party libs
import salt.ext.six as six
STATE_DIR = os.path.join(integration.FILES, 'file', 'base')
@ -279,6 +279,65 @@ class FileTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
os.remove(funny_file)
os.remove(funny_url_path)
def test_managed_contents(self):
'''
test file.managed with contents that is a boolean, string, integer,
float, list, and dictionary
'''
state_name = 'file-FileTest-test_managed_contents'
state_filename = state_name + '.sls'
state_file = os.path.join(STATE_DIR, state_filename)
managed_files = {}
state_keys = {}
for typ in ('bool', 'str', 'int', 'float', 'list', 'dict'):
managed_files[typ] = tempfile.mkstemp()[1]
state_keys[typ] = 'file_|-{0} file_|-{1}_|-managed'.format(typ, managed_files[typ])
try:
salt.utils.fopen(state_file, 'w').write(textwrap.dedent('''\
bool file:
file.managed:
- name: {bool}
- contents: True
str file:
file.managed:
- name: {str}
- contents: Salt was here.
int file:
file.managed:
- name: {int}
- contents: 340282366920938463463374607431768211456
float file:
file.managed:
- name: {float}
- contents: 1.7518e-45 # gravitational coupling constant
list file:
file.managed:
- name: {list}
- contents: [1, 1, 2, 3, 5, 8, 13]
dict file:
file.managed:
- name: {dict}
- contents:
C: charge
P: parity
T: time
'''.format(**managed_files)))
ret = self.run_function('state.sls', [state_name])
for typ in state_keys:
self.assertTrue(ret[state_keys[typ]]['result'])
self.assertIn('diff', ret[state_keys[typ]]['changes'])
finally:
os.remove(state_file)
for typ in managed_files:
os.remove(managed_files[typ])
def test_directory(self):
'''
file.directory

View file

@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.mock import (
patch,
NO_MOCK,
NO_MOCK_REASON
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.modules import win_groupadd
# Import Other Libs
# pylint: disable=W0611
try:
import win32com
import pythoncom
import pywintypes
HAS_WIN_LIBS = True
except ImportError:
HAS_WIN_LIBS = False
# pylint: enable=W0611
win_groupadd.__context__ = {}
win_groupadd.__opts__ = {'test': False}
@skipIf(not HAS_WIN_LIBS, 'win_groupadd unit tests can only be run if win32com, pythoncom, and pywintypes are installed')
@skipIf(NO_MOCK, NO_MOCK_REASON)
class WinGroupTestCase(TestCase):
'''
Test cases for salt.modules.win_groupadd
'''
# 'add' function tests: 1
def test_add(self):
'''
Test if it add the specified group
'''
self.assertDictEqual(win_groupadd.add('foo'),
{'changes': [], 'name': 'foo', 'result': None,
'comment': 'The group foo already exists.'})
# 'delete' function tests: 1
def test_delete(self):
'''
Test if it remove the specified group
'''
self.assertDictEqual(win_groupadd.delete('foo'),
{'changes': [], 'name': 'foo', 'result': None,
'comment': 'The group foo does not exists.'})
# 'info' function tests: 1
def test_info(self):
'''
Test if it return information about a group.
'''
with patch(win_groupadd.win32.client, 'flag', None):
self.assertDictEqual(win_groupadd.info('dc=salt'),
{'gid': None,
'members': ['dc=\\user1'],
'passwd': None,
'name': 'WinNT://./dc=salt,group'})
with patch(win_groupadd.win32.client, 'flag', 1):
self.assertFalse(win_groupadd.info('dc=salt'))
with patch(win_groupadd.win32.client, 'flag', 2):
self.assertFalse(win_groupadd.info('dc=salt'))
# 'getent' function tests: 1
def test_getent(self):
'''
Test if it return info on all groups
'''
with patch.dict(win_groupadd.__context__, {'group.getent': True}):
self.assertTrue(win_groupadd.getent())
# 'adduser' function tests: 1
def test_adduser(self):
'''
Test if it add a user to a group
'''
with patch(win_groupadd.win32.client, 'flag', None):
self.assertDictEqual(win_groupadd.adduser('dc=foo', 'dc=\\username'),
{'changes': {'Users Added': ['dc=\\username']},
'comment': '', 'name': 'dc=foo', 'result': True})
with patch(win_groupadd.win32.client, 'flag', 1):
comt = ('Failed to add dc=\\username to group dc=foo. C')
self.assertDictEqual(win_groupadd.adduser('dc=foo', 'dc=\\username'),
{'changes': {'Users Added': []}, 'name': 'dc=foo',
'comment': comt, 'result': False})
# 'deluser' function tests: 1
def test_deluser(self):
'''
Test if it remove a user to a group
'''
ret = {'changes': {'Users Removed': []},
'comment': 'User dc=\\username is not a member of dc=foo',
'name': 'dc=foo', 'result': None}
self.assertDictEqual(win_groupadd.deluser('dc=foo', 'dc=\\username'),
ret)
# 'members' function tests: 1
def test_members(self):
'''
Test if it remove a user to a group
'''
comment = ['Failure accessing group dc=foo. C']
ret = {'name': 'dc=foo', 'result': False, 'comment': comment,
'changes': {'Users Added': [], 'Users Removed': []}}
with patch(win_groupadd.win32.client, 'flag', 2):
self.assertDictEqual(win_groupadd.members
('dc=foo', 'dc=\\user1,dc=\\user2,dc=\\user3'),
ret)
with patch(win_groupadd.win32.client, 'flag', 1):
comment = ['Failed to add dc=\\user2 to dc=foo. C',
'Failed to remove dc=\\user1 from dc=foo. C']
ret.update({'comment': comment, 'result': False})
self.assertDictEqual(win_groupadd.members('dc=foo', 'dc=\\user2'), ret)
with patch(win_groupadd.win32.client, 'flag', None):
comment = ['dc=foo membership is correct']
ret.update({'comment': comment, 'result': None})
self.assertDictEqual(win_groupadd.members('dc=foo', 'dc=\\user1'), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(WinGroupTestCase, needs_daemon=False)

View file

@ -733,7 +733,7 @@ class FileTestCase(TestCase):
self.assertDictEqual(filestate.recurse(name, source), ret)
with patch.object(os.path, 'isabs', mock_t):
comt = ("'source' parameter is not a string or list of strings")
comt = ("Invalid source '1' (must be a salt:// URI)")
ret.update({'comment': comt})
self.assertDictEqual(filestate.recurse(name, 1), ret)
@ -998,10 +998,7 @@ class FileTestCase(TestCase):
ret)
ret.pop('data', None)
comt = ('Given text is not a string or a list of strings')
ret.update({'comment': comt, 'name': name})
self.assertDictEqual(filestate.append(name), ret)
ret.update({'name': name})
with patch.object(salt.utils, 'fopen',
MagicMock(mock_open(read_data=''))):
comt = ('No text found to append. Nothing appended')
@ -1082,10 +1079,7 @@ class FileTestCase(TestCase):
ret)
ret.pop('data', None)
comt = ('Given text is not a string or a list of strings')
ret.update({'comment': comt, 'name': name})
self.assertDictEqual(filestate.prepend(name), ret)
ret.update({'name': name})
with patch.object(salt.utils, 'fopen',
MagicMock(mock_open(read_data=''))):
with patch.object(salt.utils, 'istextfile', mock_f):

View file

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import postgres_database
postgres_database.__opts__ = {}
postgres_database.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresDatabaseTestCase(TestCase):
'''
Test cases for salt.states.postgres_database
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure that the named database is present
with the specified properties.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(return_value={name: {}})
with patch.dict(postgres_database.__salt__,
{'postgres.db_list': mock,
'postgres.db_alter': mock_t}):
comt = ('Database {0} is already present'.format(name))
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(postgres_database.present(name), ret)
comt = ("Database frank has wrong parameters "
"which couldn't be changed on fly.")
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(postgres_database.present(name, tablespace='A',
lc_collate=True),
ret)
with patch.dict(postgres_database.__opts__, {'test': True}):
comt = ('Database frank exists, '
'but parameters need to be changed')
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_database.present(name,
tablespace='A'),
ret)
with patch.dict(postgres_database.__opts__, {'test': False}):
comt = ('Parameters for database frank have been changed')
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Parameters changed'}})
self.assertDictEqual(postgres_database.present(name,
tablespace='A'),
ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure that the named database is absent.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(side_effect=[True, True, False])
with patch.dict(postgres_database.__salt__,
{'postgres.db_exists': mock,
'postgres.db_remove': mock_t}):
with patch.dict(postgres_database.__opts__, {'test': True}):
comt = ('Database {0} is set to be removed'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_database.absent(name), ret)
with patch.dict(postgres_database.__opts__, {'test': False}):
comt = ('Database {0} has been removed'.format(name))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Absent'}})
self.assertDictEqual(postgres_database.absent(name), ret)
comt = ('Database {0} is not present, so it cannot be removed'
.format(name))
ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(postgres_database.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresDatabaseTestCase, needs_daemon=False)

View file

@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import postgres_extension
postgres_extension.__opts__ = {}
postgres_extension.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresExtensionTestCase(TestCase):
'''
Test cases for salt.states.postgres_extension
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure that the named extension is present
with the specified privileges.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock = MagicMock(return_value={})
with patch.dict(postgres_extension.__salt__,
{'postgres.create_metadata': mock}):
with patch.dict(postgres_extension.__opts__, {'test': True}):
comt = ('Extension {0} is set to be created'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_extension.present(name), ret)
with patch.dict(postgres_extension.__opts__, {'test': False}):
comt = ('Extension {0} is already present'.format(name))
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(postgres_extension.present(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure that the named extension is absent.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(side_effect=[True, False])
mock = MagicMock(side_effect=[True, True, True, False])
with patch.dict(postgres_extension.__salt__,
{'postgres.is_installed_extension': mock,
'postgres.drop_extension': mock_t}):
with patch.dict(postgres_extension.__opts__, {'test': True}):
comt = ('Extension {0} is set to be removed'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_extension.absent(name), ret)
with patch.dict(postgres_extension.__opts__, {'test': False}):
comt = ('Extension {0} has been removed'.format(name))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Absent'}})
self.assertDictEqual(postgres_extension.absent(name), ret)
comt = ('Extension {0} failed to be removed'.format(name))
ret.update({'comment': comt, 'result': False, 'changes': {}})
self.assertDictEqual(postgres_extension.absent(name), ret)
comt = ('Extension {0} is not present, so it cannot be removed'
.format(name))
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(postgres_extension.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresExtensionTestCase, needs_daemon=False)

View file

@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import postgres_group
postgres_group.__opts__ = {}
postgres_group.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresGroupTestCase(TestCase):
'''
Test cases for salt.states.postgres_group
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure that the named group is present
with the specified privileges.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(return_value=None)
with patch.dict(postgres_group.__salt__,
{'postgres.role_get': mock,
'postgres.group_create': mock_t}):
with patch.dict(postgres_group.__opts__, {'test': True}):
comt = ('Group {0} is set to be created'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_group.present(name), ret)
with patch.dict(postgres_group.__opts__, {'test': False}):
comt = ('The group {0} has been created'.format(name))
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(postgres_group.present(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure that the named group is absent.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(side_effect=[True, True, False])
with patch.dict(postgres_group.__salt__,
{'postgres.user_exists': mock,
'postgres.group_remove': mock_t}):
with patch.dict(postgres_group.__opts__, {'test': True}):
comt = ('Group {0} is set to be removed'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_group.absent(name), ret)
with patch.dict(postgres_group.__opts__, {'test': False}):
comt = ('Group {0} has been removed'.format(name))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Absent'}})
self.assertDictEqual(postgres_group.absent(name), ret)
comt = ('Group {0} is not present, so it cannot be removed'
.format(name))
ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(postgres_group.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresGroupTestCase, needs_daemon=False)

View file

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import postgres_schema
postgres_schema.__opts__ = {}
postgres_schema.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresSchemaTestCase(TestCase):
'''
Test cases for salt.states.postgres_schema
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure that the named schema is present in the database.
'''
name = 'myname'
dbname = 'mydb'
ret = {'name': name,
'dbname': dbname,
'changes': {},
'result': True,
'comment': ''}
mock = MagicMock(return_value=name)
with patch.dict(postgres_schema.__salt__,
{'postgres.schema_get': mock}):
comt = ('Schema {0} already exists in database {1}'.format(name,
dbname))
ret.update({'comment': comt})
self.assertDictEqual(postgres_schema.present(dbname, name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure that the named schema is absent.
'''
name = 'myname'
dbname = 'mydb'
ret = {'name': name,
'dbname': dbname,
'changes': {},
'result': True,
'comment': ''}
mock_t = MagicMock(side_effect=[True, False])
mock = MagicMock(side_effect=[True, True, False])
with patch.dict(postgres_schema.__salt__,
{'postgres.schema_exists': mock,
'postgres.schema_remove': mock_t}):
comt = ('Schema {0} has been removed from database {1}'.
format(name, dbname))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Absent'}})
self.assertDictEqual(postgres_schema.absent(dbname, name), ret)
comt = ('Schema {0} failed to be removed'.format(name))
ret.update({'comment': comt, 'result': False, 'changes': {}})
self.assertDictEqual(postgres_schema.absent(dbname, name), ret)
comt = ('Schema {0} is not present in database {1},'
' so it cannot be removed'.format(name, dbname))
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(postgres_schema.absent(dbname, name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresSchemaTestCase, needs_daemon=False)

View file

@ -33,9 +33,9 @@ MODS = (
OPTS = {'test': False}
for postgres in MODS:
postgres.__grains__ = None # in order to stub it w/patch below
postgres.__salt__ = None # in order to stub it w/patch below
postgres.__opts__ = OPTS # in order to stub it w/patch below
postgres.__grains__ = {} # in order to stub it w/patch below
postgres.__salt__ = {} # in order to stub it w/patch below
postgres.__opts__ = {} # in order to stub it w/patch below
if NO_MOCK is False:
SALT_STUB = {
@ -51,7 +51,8 @@ else:
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch.multiple(postgres_user,
__grains__={'os_family': 'Linux'},
__salt__=SALT_STUB)
__salt__=SALT_STUB,
__opts__={'test': False})
@patch('salt.utils.which', Mock(return_value='/usr/bin/pgsql'))
class PostgresUserTestCase(TestCase):
@ -61,7 +62,7 @@ class PostgresUserTestCase(TestCase):
})
def test_present__creation(self):
# test=True
with patch.dict(OPTS, {'test': True}):
with patch.dict(postgres_user.__opts__, {'test': True}):
ret = postgres_user.present('foo')
self.assertEqual(
ret,
@ -111,7 +112,7 @@ class PostgresUserTestCase(TestCase):
})
def test_present__update(self):
# test=True
with patch.dict(OPTS, {'test': True}):
with patch.dict(postgres_user.__opts__, {'test': True}):
ret = postgres_user.present('foo', login=True, replication=False)
self.assertEqual(
ret,
@ -183,7 +184,8 @@ class PostgresUserTestCase(TestCase):
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch.multiple(postgres_group,
__grains__={'os_family': 'Linux'},
__salt__=SALT_STUB)
__salt__=SALT_STUB,
__opts__={'test': False})
@patch('salt.utils.which', Mock(return_value='/usr/bin/pgsql'))
class PostgresGroupTestCase(TestCase):
@ -193,7 +195,7 @@ class PostgresGroupTestCase(TestCase):
})
def test_present__creation(self):
# test=True
with patch.dict(OPTS, {'test': True}):
with patch.dict(postgres_group.__opts__, {'test': True}):
ret = postgres_group.present('foo')
self.assertEqual(
ret,
@ -243,7 +245,7 @@ class PostgresGroupTestCase(TestCase):
})
def test_present__update(self):
# test=True
with patch.dict(OPTS, {'test': True}):
with patch.dict(postgres_group.__opts__, {'test': True}):
ret = postgres_group.present('foo', login=True, replication=False)
self.assertEqual(
ret,
@ -315,7 +317,8 @@ class PostgresGroupTestCase(TestCase):
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch.multiple(postgres_extension,
__grains__={'os_family': 'Linux'},
__salt__=SALT_STUB)
__salt__=SALT_STUB,
__opts__={'test': False})
@patch('salt.utils.which', Mock(return_value='/usr/bin/pgsql'))
class PostgresExtensionTestCase(TestCase):
@ -399,26 +402,27 @@ class PostgresExtensionTestCase(TestCase):
scenario of creating upgrading extensions with possible schema and
version specifications
'''
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': 'Extension foo is set to be installed',
'changes': {}, 'name': 'foo', 'result': None}
with patch.dict(postgres_extension.__opts__, {'test': True}):
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': 'Extension foo is set to be installed',
'changes': {}, 'name': 'foo', 'result': None}
)
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': "Extension foo is set to be created",
'changes': {}, 'name': 'foo', 'result': None}
)
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': "Extension foo is set to be created",
'changes': {}, 'name': 'foo', 'result': None}
)
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': "Extension foo is set to be upgraded",
'changes': {}, 'name': 'foo', 'result': None}
)
)
ret = postgres_extension.present('foo')
self.assertEqual(
ret,
{'comment': "Extension foo is set to be upgraded",
'changes': {}, 'name': 'foo', 'result': None}
)
@patch.dict(SALT_STUB, {
'postgres.is_installed_extension': Mock(side_effect=[
@ -480,7 +484,8 @@ class PostgresExtensionTestCase(TestCase):
]),
})
def test_absent_failedtest(self):
ret = postgres_extension.absent('foo')
with patch.dict(postgres_extension.__opts__, {'test': True}):
ret = postgres_extension.absent('foo')
self.assertEqual(
ret,
{'comment': 'Extension foo is set to be removed',
@ -491,7 +496,8 @@ class PostgresExtensionTestCase(TestCase):
@skipIf(NO_MOCK, NO_MOCK_REASON)
@patch.multiple(postgres_schema,
__grains__={'os_family': 'Linux'},
__salt__=SALT_STUB)
__salt__=SALT_STUB,
__opts__={'test': False})
@patch('salt.utils.which', Mock(return_value='/usr/bin/pgsql'))
class PostgresSchemaTestCase(TestCase):

View file

@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.states import postgres_user
postgres_user.__opts__ = {}
postgres_user.__salt__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresUserTestCase(TestCase):
'''
Test cases for salt.states.postgres_user
'''
# 'present' function tests: 1
def test_present(self):
'''
Test to ensure that the named user is present
with the specified privileges.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(return_value=None)
with patch.dict(postgres_user.__salt__,
{'postgres.role_get': mock,
'postgres.user_create': mock_t}):
with patch.dict(postgres_user.__opts__, {'test': True}):
comt = ('User {0} is set to be created'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_user.present(name), ret)
with patch.dict(postgres_user.__opts__, {'test': False}):
comt = ('The user {0} has been created'.format(name))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Present'}})
self.assertDictEqual(postgres_user.present(name), ret)
# 'absent' function tests: 1
def test_absent(self):
'''
Test to ensure that the named user is absent.
'''
name = 'frank'
ret = {'name': name,
'changes': {},
'result': False,
'comment': ''}
mock_t = MagicMock(return_value=True)
mock = MagicMock(side_effect=[True, True, False])
with patch.dict(postgres_user.__salt__,
{'postgres.user_exists': mock,
'postgres.user_remove': mock_t}):
with patch.dict(postgres_user.__opts__, {'test': True}):
comt = ('User {0} is set to be removed'.format(name))
ret.update({'comment': comt, 'result': None})
self.assertDictEqual(postgres_user.absent(name), ret)
with patch.dict(postgres_user.__opts__, {'test': False}):
comt = ('User {0} has been removed'.format(name))
ret.update({'comment': comt, 'result': True,
'changes': {name: 'Absent'}})
self.assertDictEqual(postgres_user.absent(name), ret)
comt = ('User {0} is not present, so it cannot be removed'
.format(name))
ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(postgres_user.absent(name), ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresUserTestCase, needs_daemon=False)