Merge pull request #36883 from rallytime/merge-develop

[develop] Merge forward from carbon to develop
This commit is contained in:
Nicole Thomas 2016-10-10 11:39:14 -06:00 committed by GitHub
commit 33c3b8a6c3
22 changed files with 319 additions and 131 deletions

View File

@ -275,9 +275,9 @@ Linux/Unix
- name: salt-minion
- require:
- pkg: salt-minion
cmd.wait:
cmd.run:
- name: echo service salt-minion restart | at now + 1 minute
- watch:
- onchanges:
- pkg: salt-minion
To ensure that **at** is installed and **atd** is running, the following states

View File

@ -20,6 +20,7 @@ execution modules
aliases
alternatives
apache
apcups
apf
aptpkg
archive

View File

@ -0,0 +1,6 @@
===================
salt.modules.apcups
===================
.. automodule:: salt.modules.apcups
:members:

View File

@ -389,6 +389,51 @@ a useful way to execute a post hook after changing aspects of a system.
If a state has multiple ``onchanges`` requisites then the state will trigger
if any of the watched states changes.
.. note::
One easy-to-make mistake is to use ``onchanges_in`` when ``onchanges`` is
supposed to be used. For example, the below configuration is not correct:
.. code-block:: yaml
myservice:
pkg.installed:
- name: myservice
file.managed:
- name: /etc/myservice/myservice.conf
- source: salt://myservice/files/myservice.conf
- mode: 600
cmd.run:
- name: /usr/libexec/myservice/post-changes-hook.sh
- onchanges_in:
- file: /etc/myservice/myservice.conf
This will set up a requisite relationship in which the ``cmd.run`` state
always executes, and the ``file.managed`` state only executes if the
``cmd.run`` state has changes (which it always will, since the ``cmd.run``
state includes the command results as changes).
It may semantically seem like the the ``cmd.run`` state should only run
when there are changes in the file state, but remember that requisite
relationships involve one state watching another state, and a
:ref:`requisite_in <requisites-onchanges-in>` does the opposite: it forces
the specified state to watch the state with the ``requisite_in``.
The correct usage would be:
.. code-block:: yaml
myservice:
pkg.installed:
- name: myservice
file.managed:
- name: /etc/myservice/myservice.conf
- source: salt://myservice/files/myservice.conf
- mode: 600
cmd.run:
- name: /usr/libexec/myservice/post-changes-hook.sh
- onchanges:
- file: /etc/myservice/myservice.conf
use
~~~
@ -423,6 +468,7 @@ inherit inherited options.
.. _requisites-require-in:
.. _requisites-watch-in:
.. _requisites-onchanges-in:
The _in versions of requisites
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -4,6 +4,21 @@
Salt Release Notes - Codename Carbon
====================================
Release Candidate
=================
See :ref:`Installing/Testing a Salt Release Candidate <release-candidate>` for instructions to install the latest release candidate.
Release Candidate Known Issues
------------------------------
- :issue:`36729`: Minion does not shutdown properly when attempting to start multiple minions on same host
- :issue:`36693`: /etc/salt/master.d/schedule.conf schedule jobs fail to run
- :issue:`36746`: When killing a job jid output missing
- :issue:`36748`: Proxy minion is not working
- :issue:`36134`: multi-master with failover does not failover when master goes down
- :issue:`36804`: error when using pkg.installed with url source
New Features
============

View File

@ -8,15 +8,21 @@ information about the version numbering scheme.
Latest Branch Release
=====================
|current_release_doc|
.. after carbon releases, replace :ref:`Release Candidate` with the following:
|current_release_doc|
:ref:`Release Candidate <release-candidate>`
Previous Releases
=================
.. after carbon releases, remove carbon from the list below:
.. releasestree::
:maxdepth: 1
:glob:
carbon
2016.3.*
2015.8.*
2015.5.*

View File

@ -1,12 +1,14 @@
:orphan:
.. _release-candidate:
===========================================
Installing/Testing a Salt Release Candidate
===========================================
It's time for a new feature release of Salt! Follow the instructions below to
install the latest release candidate of Salt, and try :doc:`all the shiny new
features </topics/releases/2016.3.0>`! Be sure to report any bugs you find on `Github
features </topics/releases/carbon>`! Be sure to report any bugs you find on `Github
<https://github.com/saltstack/salt/issues/new/>`_.
Installing Using Packages
@ -49,14 +51,14 @@ You can install a release candidate of Salt using `Salt Bootstrap
.. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P git v2016.3.0rc2
sudo sh install_salt.sh -P git v2016.11.0rc1
If you want to also install a master using Salt Bootstrap, use the ``-M`` flag:
.. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P -M git v2016.3.0rc2
sudo sh install_salt.sh -P -M git v2016.11.0rc1
If you want to install only a master and not a minion using Salt Bootstrap, use
the ``-M`` and ``-N`` flags:
@ -64,13 +66,13 @@ the ``-M`` and ``-N`` flags:
.. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P -M -N git v2016.3.0rc2
sudo sh install_salt.sh -P -M -N git v2016.11.0rc1
Installing Using PyPI
=====================
Installing from the `source archive
<https://pypi.python.org/packages/source/s/salt/salt-2016.3.0rc2.tar.gz>`_ on
<https://pypi.python.org/packages/source/s/salt/salt-v2016.11.0rc1.tar.gz>`_ on
`PyPI <https://pypi.python.org/pypi>`_ is fairly straightforward.
.. note::
@ -108,4 +110,4 @@ Then install salt using the following command:
.. code-block:: bash
sudo pip install salt==2016.3.0rc2
sudo pip install salt==v2016.11.0rc1

View File

@ -43,7 +43,6 @@ import urllib
# Import salt cloud libs
import salt.config as config
import salt.ext.six as six
import salt.utils.cloud
from salt.exceptions import (
SaltCloudConfigError,
SaltCloudSystemExit
@ -163,7 +162,7 @@ def list_nodes_select(conn=None, call=None):
'''
Return a list of the VMs that are on the provider, with select fields
'''
return salt.utils.cloud.list_nodes_select(
return __utils__['cloud.list_nodes_select'](
list_nodes_full(), __opts__['query.selection'], call,
)
@ -207,7 +206,7 @@ def show_instance(name, call=None):
# Find under which cloud service the name is listed, if any
if name not in nodes:
return {}
salt.utils.cloud.cache_node(nodes[name], __active_provider_name__, __opts__)
__utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__)
return nodes[name]
@ -241,7 +240,7 @@ def create(vm_):
else:
enable_private_network = 'no'
salt.utils.cloud.fire_event(
__utils__['cloud.fire_event'](
'event',
'starting create',
'salt/cloud/{0}/creating'.format(vm_['name']),
@ -280,7 +279,7 @@ def create(vm_):
log.info('Creating Cloud VM {0}'.format(vm_['name']))
salt.utils.cloud.fire_event(
__utils__['cloud.fire_event'](
'event',
'requesting instance',
'salt/cloud/{0}/requesting'.format(vm_['name']),
@ -297,7 +296,7 @@ def create(vm_):
log.error('Status 412 may mean that you are requesting an\n'
'invalid location, image, or size.')
salt.utils.cloud.fire_event(
__utils__['cloud.fire_event'](
'event',
'instance request failed',
'salt/cloud/{0}/requesting/failed'.format(vm_['name']),
@ -316,7 +315,7 @@ def create(vm_):
# Show the traceback if the debug logging level is enabled
exc_info_on_loglevel=logging.DEBUG
)
salt.utils.cloud.fire_event(
__utils__['cloud.fire_event'](
'event',
'instance request failed',
'salt/cloud/{0}/requesting/failed'.format(vm_['name']),
@ -348,12 +347,12 @@ def create(vm_):
return False
return data['default_password']
vm_['ssh_host'] = salt.utils.cloud.wait_for_fun(
vm_['ssh_host'] = __utils__['cloud.wait_for_fun'](
wait_for_hostname,
timeout=config.get_cloud_config_value(
'wait_for_fun_timeout', vm_, __opts__, default=15 * 60),
)
vm_['password'] = salt.utils.cloud.wait_for_fun(
vm_['password'] = __utils__['cloud.wait_for_fun'](
wait_for_default_password,
timeout=config.get_cloud_config_value(
'wait_for_fun_timeout', vm_, __opts__, default=15 * 60),
@ -367,7 +366,7 @@ def create(vm_):
)
# Bootstrap
ret = salt.utils.cloud.bootstrap(vm_, __opts__)
ret = __utils__['cloud.bootstrap'](vm_, __opts__)
ret.update(show_instance(vm_['name'], call='action'))
@ -378,7 +377,7 @@ def create(vm_):
)
)
salt.utils.cloud.fire_event(
__utils__['cloud.fire_event'](
'event',
'created instance',
'salt/cloud/{0}/created'.format(vm_['name']),
@ -420,7 +419,7 @@ def _query(path, method='GET', data=None, params=None, header_dict=None, decode=
if header_dict is None:
header_dict = {}
result = salt.utils.http.query(
result = __utils__['http.query'](
url,
method=method,
params=params,

View File

@ -10,6 +10,10 @@ from __future__ import absolute_import
import os
import logging
# Import Salt libs
import salt.utils
__outputter__ = {
'display': 'txt',
'install': 'txt',
@ -59,6 +63,39 @@ def display(name):
return out['stdout']
def show_link(name):
'''
Display master link for the alternative
.. versionadded:: 2015.8.13,2016.3.4,Carbon
CLI Example:
.. code-block:: bash
salt '*' alternatives.show_link editor
'''
path = '/var/lib/dpkg/alternatives/{0}'.format(name)
if _get_cmd() == 'alternatives':
path = '/var/lib/alternatives/{0}'.format(name)
try:
with salt.utils.fopen(path, 'rb') as r_file:
return r_file.readlines()[1].rstrip('\n')
except OSError:
log.error(
'alternatives: {0} does not exist'.format(name)
)
except (IOError, IndexError) as exc:
log.error(
'alternatives: unable to get master link for {0}. '
'Exception: {1}'.format(name, exc)
)
return False
def show_current(name):
'''
Display the current highest-priority alternative for a given alternatives

View File

@ -10,6 +10,8 @@ Edit ini files
(for example /etc/sysctl.conf)
'''
from __future__ import absolute_import, print_function
# Import Python libs
from __future__ import print_function
from __future__ import absolute_import
@ -20,6 +22,7 @@ import json
# Import Salt libs
import salt.ext.six as six
import salt.utils
from salt.exceptions import CommandExecutionError
from salt.utils.odict import OrderedDict
@ -50,8 +53,8 @@ def set_option(file_name, sections=None, separator='='):
A dictionary representing the sections to be edited ini file
The keys are the section names and the values are the dictionary
containing the options
If the Ini does not contain sections the keys and values represent the
options
If the ini file does not contain sections the keys and values represent
the options
separator : =
A character used to separate keys and values. Standard ini files use
@ -364,7 +367,13 @@ class _Ini(_Section):
super(_Ini, self).__init__(name, inicontents, separator, commenter)
def refresh(self, inicontents=None):
inicontents = inicontents or salt.utils.fopen(self.name).read()
try:
inicontents = inicontents or salt.utils.fopen(self.name).read()
except (OSError, IOError) as exc:
raise CommandExecutionError(
"Unable to open file '{0}'. "
"Exception: {1}".format(self.name, exc)
)
if not inicontents:
return
# Remove anything left behind from a previous run.
@ -383,10 +392,16 @@ class _Ini(_Section):
self.update({sect_obj.name: sect_obj})
def flush(self):
with salt.utils.fopen(self.name, 'w') as outfile:
ini_gen = self.gen_ini()
next(ini_gen)
outfile.writelines(ini_gen)
try:
with salt.utils.fopen(self.name, 'w') as outfile:
ini_gen = self.gen_ini()
next(ini_gen)
outfile.writelines(ini_gen)
except (OSError, IOError) as exc:
raise CommandExecutionError(
"Unable to write file '{0}'. "
"Exception: {1}".format(self.name, exc)
)
@staticmethod
def get_ini_file(file_name, separator='='):

View File

@ -333,6 +333,7 @@ def _psql_cmd(*args, **kwargs):
cmd = [_PSQL_BIN,
'--no-align',
'--no-readline',
'--no-psqlrc',
'--no-password'] # It is never acceptable to issue a password prompt.
if user:
cmd += ['--username', user]

View File

@ -666,21 +666,27 @@ def version_cmp(ver1, ver2, ignore_epoch=False):
ver1 = _ensure_epoch(ver1)
ver2 = _ensure_epoch(ver2)
result = __salt__['cmd.run'](['rpmdev-vercmp', ver1, ver2],
python_shell=False,
ignore_retcode=True).strip()
if result.endswith('equal'):
result = __salt__['cmd.run_all'](
['rpmdev-vercmp', ver1, ver2],
python_shell=False,
redirect_stderr=True,
ignore_retcode=True)
# rpmdev-vercmp returns 0 on equal, 11 on greater-than, and
# 12 on less-than.
if result['retcode'] == 0:
return 0
elif 'is newer' in result:
newer_version = result.split()[0]
if newer_version == ver1:
return 1
elif newer_version == ver2:
return -1
log.warning(
'Failed to interpret results of rpmdev-vercmp output: %s',
result
)
elif result['retcode'] == 11:
return 1
elif result['retcode'] == 12:
return -1
else:
# We'll need to fall back to salt.utils.version_cmp()
log.warning(
'Failed to interpret results of rpmdev-vercmp output. '
'This is probably a bug, and should be reported. '
'Return code was %s. Output: %s',
result['retcode'], result['stdout']
)
else:
# We'll need to fall back to salt.utils.version_cmp()
log.warning(

View File

@ -1259,7 +1259,10 @@ def sls_id(
opts['test'] = _get_test_value(test, **kwargs)
if 'pillarenv' in kwargs:
opts['pillarenv'] = kwargs['pillarenv']
st_ = salt.state.HighState(opts)
try:
st_ = salt.state.HighState(opts, proxy=__proxy__)
except NameError:
st_ = salt.state.HighState(opts)
if isinstance(mods, six.string_types):
split_mods = mods.split(',')
st_.push_active()

View File

@ -15,6 +15,10 @@ import subprocess
import salt.utils
def __virtual__():
return salt.utils.which('certtool') is not None
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
command): # pylint: disable=W0613

View File

@ -33,6 +33,13 @@ __func_alias__ = {
}
def __virtual__():
'''
Only load if alternatives execution module is available.
'''
return True if 'alternatives.auto' in __salt__ else False
def install(name, link, path, priority):
'''
Install new alternative for defined <name>
@ -63,24 +70,33 @@ def install(name, link, path, priority):
'comment': ''}
isinstalled = __salt__['alternatives.check_installed'](name, path)
if not isinstalled:
if isinstalled:
ret['comment'] = 'Alternatives for {0} is already set to {1}'.format(name, path)
else:
if __opts__['test']:
ret['comment'] = (
'Alternative will be set for {0} to {1} with priority {2}'
).format(name, path, priority)
ret['result'] = None
return ret
__salt__['alternatives.install'](name, link, path, priority)
ret['comment'] = (
'Setting alternative for {0} to {1} with priority {2}'
).format(name, path, priority)
ret['changes'] = {'name': name,
'link': link,
'path': path,
'priority': priority}
return ret
ret['comment'] = 'Alternatives for {0} is already set to {1}'.format(name, path)
out = __salt__['alternatives.install'](name, link, path, priority)
current = __salt__['alternatives.show_current'](name)
master_link = __salt__['alternatives.show_link'](name)
if current == path and master_link == link:
ret['comment'] = (
'Alternative for {0} set to path {1} with priority {2}'
).format(name, current, priority)
ret['changes'] = {'name': name,
'link': link,
'path': path,
'priority': priority}
else:
ret['result'] = False
ret['comment'] = (
'Alternative for {0} not installed: {1}'
).format(name, out)
return ret
@ -214,7 +230,6 @@ def set_(name, path):
__salt__['alternatives.set'](name, path)
current = __salt__['alternatives.show_current'](name)
if current == path:
ret['result'] = True
ret['comment'] = (
'Alternative for {0} set to path {1}'
).format(name, current)

View File

@ -12,13 +12,23 @@ import time
# Import OpenStack libs
try:
from keystoneclient.apiclient.exceptions import \
from keystoneclient.exceptions import \
Unauthorized as kstone_Unauthorized
HAS_KEYSTONE = True
except ImportError:
try:
from keystoneclient.apiclient.exceptions import \
Unauthorized as kstone_Unauthorized
HAS_KEYSTONE = True
except ImportError:
HAS_KEYSTONE = False
try:
from glanceclient.exc import \
HTTPUnauthorized as glance_Unauthorized
HAS_DEPENDENCIES = True
HAS_GLANCE = True
except ImportError:
HAS_DEPENDENCIES = False
HAS_GLANCE = False
log = logging.getLogger(__name__)
@ -27,7 +37,7 @@ def __virtual__():
'''
Only load if dependencies are loaded
'''
return HAS_DEPENDENCIES
return HAS_KEYSTONE and HAS_GLANCE
def _find_image(name):
@ -38,20 +48,23 @@ def _find_image(name):
- False, 'Found more than one image with given name'
'''
try:
images_dict = __salt__['glance.image_list'](name=name)
images = __salt__['glance.image_list'](name=name)
except kstone_Unauthorized:
return False, 'keystoneclient: Unauthorized'
except glance_Unauthorized:
return False, 'glanceclient: Unauthorized'
log.debug('Got images_dict: {0}'.format(images_dict))
log.debug('Got images: {0}'.format(images))
# I /think/ this will still work when glance.image_list
# starts returning a list instead of a dictionary...
if len(images_dict) == 0:
if type(images) is dict and len(images) == 1 and 'images' in images:
images = images['images']
images_list = images.values() if type(images) is dict else images
if len(images_list) == 0:
return None, 'No image with name "{0}"'.format(name)
elif len(images_dict) == 1:
return images_dict.values()[0], 'Found image {0}'.format(name)
elif len(images_dict) > 1:
elif len(images_list) == 1:
return images_list[0], 'Found image {0}'.format(name)
elif len(images_list) > 1:
return False, 'Found more than one image with given name'
else:
raise NotImplementedError

View File

@ -59,8 +59,10 @@ class Reactor(salt.utils.process.SignalHandlingMultiprocessingProcess, salt.stat
if glob_ref.startswith('salt://'):
glob_ref = self.minion.functions['cp.cache_file'](glob_ref)
for fn_ in glob.glob(glob_ref):
globbed_ref = glob.glob(glob_ref)
if not globbed_ref:
log.error('Can not render SLS {0} for tag {1}. File missing or not found.'.format(glob_ref, tag))
for fn_ in globbed_ref:
try:
res = self.render_template(
fn_,

View File

@ -160,6 +160,7 @@ class ArchiveTest(integration.ModuleCase):
self._tear_down()
@skipIf(not salt.utils.which('gzip'), 'Cannot find gzip executable')
@skipIf(not salt.utils.which('gunzip'), 'Cannot find gunzip executable')
def test_gunzip(self):
'''
@ -205,7 +206,7 @@ class ArchiveTest(integration.ModuleCase):
self._tear_down()
@skipIf(not HAS_ZIPFILE, 'Cannot find zip python module')
@skipIf(not HAS_ZIPFILE, 'Cannot find zipfile python module')
def test_zip(self):
'''
Validate using the zip function
@ -219,7 +220,7 @@ class ArchiveTest(integration.ModuleCase):
self._tear_down()
@skipIf(not HAS_ZIPFILE, 'Cannot find zip python module')
@skipIf(not HAS_ZIPFILE, 'Cannot find zipfile python module')
def test_unzip(self):
'''
Validate using the unzip function
@ -248,7 +249,8 @@ class ArchiveTest(integration.ModuleCase):
self._tear_down()
@skipIf(not salt.utils.which_bin(('rar', 'unrar')), 'Cannot find rar or unrar executable')
@skipIf(not salt.utils.which('rar'), 'Cannot find rar executable')
@skipIf(not salt.utils.which('unrar'), 'Cannot find unrar executable')
def test_unrar(self):
'''
Validate using the unrar function

View File

@ -45,7 +45,7 @@ class PipModuleTest(integration.ModuleCase):
'''
Checks to see if a download error looks transitory
'''
return any(w in ret for w in ['URLError'])
return any(w in ret for w in ['URLError', 'Download error'])
def pip_successful_install(self, target, expect=('flake8', 'pep8',)):
'''

View File

@ -37,7 +37,7 @@ class AlternativesTestCase(TestCase):
python_shell=False
)
with patch.dict(alternatives.__grains__, {'os_family': 'Ubuntu'}):
with patch.dict(alternatives.__grains__, {'os_family': 'Suse'}):
mock = MagicMock(
return_value={'retcode': 0, 'stdout': 'undoubtedly-salt'}
)

View File

@ -91,13 +91,13 @@ class PostgresTestCase(TestCase):
owner='otheruser',
runas='foo')
postgres._run_psql.assert_has_calls([
call(['/usr/bin/pgsql', '--no-align', '--no-readline',
call(['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'ALTER DATABASE "dbname" OWNER TO "otheruser"'],
host='testhost', user='testuser',
password='foo', runas='foo', port='testport'),
call(['/usr/bin/pgsql', '--no-align', '--no-readline',
call(['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'ALTER DATABASE "dbname" SET TABLESPACE "testspace"'],
@ -142,7 +142,7 @@ class PostgresTestCase(TestCase):
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'CREATE DATABASE "dbname" WITH TABLESPACE = "testspace" '
@ -158,7 +158,7 @@ class PostgresTestCase(TestCase):
maintenance_db='maint_db', password='foo')
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', '1234', '--dbname', 'maint_db', '-c',
'CREATE DATABASE "dbname" WITH ENCODING = \'utf8\' '
@ -233,7 +233,7 @@ class PostgresTestCase(TestCase):
runas='foo'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'DROP DATABASE "test_db"'],
@ -261,11 +261,11 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
postgres._run_psql.call_args[0][0][13].startswith('CREATE ROLE')
postgres._run_psql.call_args[0][0][14].startswith('CREATE ROLE')
)
@patch('salt.modules.postgres._run_psql',
@ -282,7 +282,7 @@ class PostgresTestCase(TestCase):
runas='foo'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'DROP ROLE "testgroup"'],
@ -310,13 +310,13 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'ALTER.* "testgroup" .* UNENCRYPTED PASSWORD',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -344,10 +344,10 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
call = postgres._run_psql.call_args[0][0][13]
# query will be in the 15th argument.
call = postgres._run_psql.call_args[0][0][14]
self.assertTrue(re.match('CREATE ROLE "testuser"', call))
for i in (
'INHERIT NOCREATEDB NOCREATEROLE '
@ -442,7 +442,7 @@ class PostgresTestCase(TestCase):
runas='foo'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'DROP ROLE "testuser"'],
@ -473,16 +473,16 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB '
'NOCREATEROLE NOREPLICATION LOGIN '
'UNENCRYPTED PASSWORD [\'"]{0,5}test_role_pass[\'"]{0,5};'
' GRANT "test_groups" TO "test_username"',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -509,15 +509,15 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB '
'CREATEROLE NOREPLICATION LOGIN;'
' GRANT "test_groups" TO "test_username"',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -545,15 +545,15 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB '
'CREATEROLE NOREPLICATION LOGIN NOPASSWORD;'
' GRANT "test_groups" TO "test_username"',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -581,9 +581,9 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB '
@ -591,7 +591,7 @@ class PostgresTestCase(TestCase):
'ENCRYPTED PASSWORD '
'[\'"]{0,5}md531c27e68d3771c392b52102c01be1da1[\'"]{0,5}'
'; GRANT "test_groups" TO "test_username"',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -607,13 +607,13 @@ class PostgresTestCase(TestCase):
runas='foo'
)
# postgres._run_psql.call_args[0][0] will contain the list of CLI args.
# The first 13 elements of this list are initial args used in all (or
# The first 14 elements of this list are initial args used in all (or
# virtually all) commands run through _run_psql(), so the actual SQL
# query will be in the 14th argument.
# query will be in the 15th argument.
self.assertTrue(
re.match(
'SELECT setting FROM pg_catalog.pg_settings',
postgres._run_psql.call_args[0][0][13]
postgres._run_psql.call_args[0][0][14]
)
)
@ -854,7 +854,7 @@ class PostgresTestCase(TestCase):
db_password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'CREATE SCHEMA "testschema"'],
@ -887,7 +887,7 @@ class PostgresTestCase(TestCase):
db_password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'maint_db',
'-c', 'DROP SCHEMA "testschema"'],
@ -962,7 +962,7 @@ class PostgresTestCase(TestCase):
password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'testdb',
'-c', 'CREATE LANGUAGE plpythonu'],
@ -1002,7 +1002,7 @@ class PostgresTestCase(TestCase):
password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'testdb',
'-c', 'DROP LANGUAGE plpgsql'],
@ -1077,7 +1077,7 @@ class PostgresTestCase(TestCase):
"ORDER BY relname) TO STDOUT WITH CSV HEADER")
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-v', 'datestyle=ISO,MDY', '-c', query],
@ -1116,7 +1116,7 @@ class PostgresTestCase(TestCase):
"TO STDOUT WITH CSV HEADER")
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-v', 'datestyle=ISO,MDY', '-c', query],
@ -1267,7 +1267,7 @@ class PostgresTestCase(TestCase):
query = 'GRANT ALL ON TABLE public."awl" TO "baruwa" WITH GRANT OPTION'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1294,7 +1294,7 @@ class PostgresTestCase(TestCase):
query = 'GRANT ALL ON TABLE public."awl" TO "baruwa"'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1322,7 +1322,7 @@ class PostgresTestCase(TestCase):
query = 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "baruwa"'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1353,7 +1353,7 @@ class PostgresTestCase(TestCase):
query = 'GRANT admins TO "baruwa" WITH ADMIN OPTION'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1379,7 +1379,7 @@ class PostgresTestCase(TestCase):
query = 'GRANT admins TO "baruwa"'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1410,7 +1410,7 @@ class PostgresTestCase(TestCase):
query = 'REVOKE ALL ON TABLE public.awl FROM baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],
@ -1440,7 +1440,7 @@ class PostgresTestCase(TestCase):
query = 'REVOKE admins FROM baruwa'
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'db_name',
'-c', query],

View File

@ -49,26 +49,33 @@ class AlternativesTestCase(TestCase):
'changes': {},
'comment': ''}
mock = MagicMock(side_effect=[True, False, False])
mock_bool = MagicMock(return_value=True)
bad_link = '/bin/pager'
err = 'the primary link for {0} must be {1}'.format(name, link)
mock = MagicMock(side_effect=[True, False, False, False])
mock_out = MagicMock(side_effect=['', err])
mock_path = MagicMock(return_value=path)
mock_link = MagicMock(return_value=link)
with patch.dict(alternatives.__salt__,
{'alternatives.check_installed': mock,
'alternatives.install': mock_bool}):
'alternatives.install': mock_out,
'alternatives.show_current': mock_path,
'alternatives.show_link': mock_link}):
comt = ('Alternatives for {0} is already set to {1}'
).format(name, path)
).format(name, path)
ret.update({'comment': comt, 'result': True})
self.assertDictEqual(alternatives.install(name, link, path,
priority), ret)
comt = (('Alternative will be set for {0} to {1} with priority {2}'
).format(name, path, priority))
).format(name, path, priority))
ret.update({'comment': comt, 'result': None})
with patch.dict(alternatives.__opts__, {'test': True}):
self.assertDictEqual(alternatives.install(name, link, path,
priority), ret)
comt = ('Setting alternative for {0} to {1} with priority {2}'
).format(name, path, priority)
comt = ('Alternative for {0} set to path {1} with priority {2}'
).format(name, path, priority)
ret.update({'comment': comt, 'result': True,
'changes': {'name': name, 'link': link, 'path': path,
'priority': priority}})
@ -76,6 +83,14 @@ class AlternativesTestCase(TestCase):
self.assertDictEqual(alternatives.install(name, link, path,
priority), ret)
comt = ('Alternative for {0} not installed: {1}'
).format(name, err)
ret.update({'comment': comt, 'result': False,
'changes': {}, 'link': bad_link})
with patch.dict(alternatives.__opts__, {'test': False}):
self.assertDictEqual(alternatives.install(name, bad_link, path,
priority), ret)
# 'remove' function tests: 1
def test_remove(self):
@ -94,10 +109,10 @@ class AlternativesTestCase(TestCase):
mock = MagicMock(side_effect=[True, True, True, False, False])
mock_bool = MagicMock(return_value=True)
mock_bol = MagicMock(side_effect=[False, True, True, False])
mock_show = MagicMock(side_effect=[False, True, True, False])
with patch.dict(alternatives.__salt__,
{'alternatives.check_exists': mock,
'alternatives.show_current': mock_bol,
'alternatives.show_current': mock_show,
'alternatives.remove': mock_bool}):
comt = ('Alternative for {0} will be removed'.format(name))
ret.update({'comment': comt})
@ -116,7 +131,7 @@ class AlternativesTestCase(TestCase):
self.assertDictEqual(alternatives.remove(name, path), ret)
comt = ('Alternative for {0} is set to it\'s default path True'
).format(name)
).format(name)
ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(alternatives.remove(name, path), ret)
@ -175,17 +190,17 @@ class AlternativesTestCase(TestCase):
mock = MagicMock(side_effect=[path, path, ''])
mock_bool = MagicMock(return_value=True)
mock_bol = MagicMock(side_effect=[path, False, False, False, False])
mock_show = MagicMock(side_effect=[path, False, False, False, False])
with patch.dict(alternatives.__salt__,
{'alternatives.display': mock,
'alternatives.show_current': mock_bol,
'alternatives.show_current': mock_show,
'alternatives.set': mock_bool}):
comt = ('Alternative for {0} already set to {1}'.format(name, path))
ret.update({'comment': comt})
self.assertDictEqual(alternatives.set_(name, path), ret)
comt = ('Alternative for {0} will be set to path False'
).format(name)
).format(name)
ret.update({'comment': comt, 'result': None})
with patch.dict(alternatives.__opts__, {'test': True}):
self.assertDictEqual(alternatives.set_(name, path), ret)