Merge branch '2016.11' into 'develop'

Conflicts:
  - doc/ref/configuration/master.rst
  - doc/topics/cloud/index.rst
  - salt/states/cron.py
This commit is contained in:
rallytime 2016-12-19 14:00:52 -07:00
commit 8f80916432
17 changed files with 147 additions and 28 deletions

View File

@ -852,6 +852,10 @@
# order will be used.
#syndic_failover: random
# The number of seconds for the salt client to wait for additional syndics to
# check in with their lists of expected minions before giving up.
#syndic_wait: 5
##### Peer Publish settings #####
##########################################

View File

@ -11,6 +11,7 @@ Full list of Salt Cloud modules
:template: autosummary.rst.tmpl
aliyun
azurearm
cloudstack
digital_ocean
dimensiondata

View File

@ -0,0 +1,6 @@
==========================
salt.cloud.clouds.azurearm
==========================
.. automodule:: salt.cloud.clouds.azurearm
:members:

View File

@ -3151,6 +3151,20 @@ order will be used.
syndic_failover: random
.. conf_master:: syndic_wait
``syndic_wait``
---------------
Default: ``5``
The number of seconds for the salt client to wait for additional syndics to
check in with their lists of expected minions before giving up.
.. code-block:: yaml
syndic_wait: 5
.. conf_master:: syndic_forward_all_events
``syndic_forward_all_events``

View File

@ -105,7 +105,9 @@ Cloud Provider Specifics
Getting Started With Aliyun <aliyun>
Getting Started With Azure <azure>
Getting Started With Azure Arm <azurearm>
Getting Started With DigitalOcean <digitalocean>
Getting Started With Dimension Data <dimensiondata>
Getting Started With EC2 <aws>
Getting Started With GoGrid <gogrid>
Getting Started With Google Compute Engine <gce>
@ -114,19 +116,19 @@ Cloud Provider Specifics
Getting Started With LXC <lxc>
Getting Started With Libvirt <libvirt>
Getting Started With Linode <linode>
Getting Started With LXC <lxc>
Getting Started With OpenNebula <opennebula>
Getting Started With OpenStack <openstack>
Getting Started With Parallels <parallels>
Getting Started With ProfitBricks <profitbricks>
Getting Started With Proxmox <proxmox>
Getting Started With Rackspace <rackspace>
Getting Started With Saltify <saltify>
Getting Started With Scaleway <scaleway>
Getting Started With Saltify <saltify>
Getting Started With SoftLayer <softlayer>
Getting Started With Vexxhost <vexxhost>
Getting Started With Virtualbox <virtualbox>
Getting Started With VMware <vmware>
Getting Started With vSphere <vsphere>
Miscellaneous Options

View File

@ -168,6 +168,16 @@ daemon until the data reaches the Master or Syndic node that issued the command.
Syndic wait
===========
``syndic_wait`` is a master configuration file setting that specifies the number of
seconds the Salt client should wait for additional syndics to check in with their
lists of expected minions before giving up. This value defaults to ``5`` seconds.
The ``syndic_wait`` setting is necessary because the higher-level master does not
have a way of knowing which minions are below the syndics. The higher-level master
has its own list of expected minions and the masters below them have their own lists
as well, so the Salt client does not how long to wait for all returns. The
``syndic_wait`` option allows time for all minions to return to the Salt client.
.. note::
To reduce the amount of time the CLI waits for Minions to respond, install

View File

@ -1319,7 +1319,7 @@ def os_data():
init_cmdline = fhr.read().replace('\x00', ' ').split()
init_bin = salt.utils.which(init_cmdline[0])
if init_bin is not None and init_bin.endswith('bin/init'):
supported_inits = (six.b('upstart'), six.b('sysvinit'), six.b('systemd'), six.b('runit'))
supported_inits = (six.b('upstart'), six.b('sysvinit'), six.b('systemd'))
edge_len = max(len(x) for x in supported_inits) - 1
try:
buf_size = __opts__['file_buffer_size']
@ -1349,6 +1349,8 @@ def os_data():
)
elif salt.utils.which('supervisord') in init_cmdline:
grains['init'] = 'supervisord'
elif init_cmdline == ['runit']:
grains['init'] = 'runit'
else:
log.info(
'Could not determine init system from command line: ({0})'

View File

@ -112,12 +112,11 @@ def show_current(name):
salt '*' alternatives.show_current editor
'''
alt_link_path = '/etc/alternatives/{0}'.format(name)
try:
return os.readlink(alt_link_path)
return _read_link(name)
except OSError:
log.error(
'alternatives: path {0} does not exist'.format(alt_link_path)
'alternative: {0} does not exist'.format(name)
)
return False
@ -154,7 +153,10 @@ def check_installed(name, path):
salt '*' alternatives.check_installed name path
'''
return show_current(name) == path
try:
return _read_link(name) == path
except OSError:
return False
def install(name, link, path, priority):
@ -224,3 +226,13 @@ def set_(name, path):
if out['retcode'] > 0:
return out['stderr']
return out['stdout']
def _read_link(name):
'''
Read the link from /etc/alternatives
Throws an OSError if the link does not exist
'''
alt_link_path = '/etc/alternatives/{0}'.format(name)
return os.readlink(alt_link_path)

View File

@ -966,6 +966,9 @@ def is_encrypted(name, clean=False, saltenv='base'):
Returns ``True`` if the zip archive is password-protected, ``False`` if
not. If the specified file is not a ZIP archive, an error will be raised.
name
The path / URL of the archive to check.
clean : False
Set this value to ``True`` to delete the path referred to by ``name``
once the contents have been listed. This option should be used with
@ -982,6 +985,7 @@ def is_encrypted(name, clean=False, saltenv='base'):
salt '*' archive.is_encrypted /path/to/myfile.zip
salt '*' archive.is_encrypted salt://foo.zip
salt '*' archive.is_encrypted salt://foo.zip saltenv=dev
salt '*' archive.is_encrypted https://domain.tld/myfile.zip clean=True
salt '*' archive.is_encrypted ftp://10.1.2.3/foo.zip
'''

View File

@ -255,6 +255,7 @@ def list_(narrow=None,
cmd.extend(['-source', source])
if local_only:
cmd.extend(['-localonly'])
cmd.extend(['-limitoutput'])
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@ -264,7 +265,7 @@ def list_(narrow=None,
raise CommandExecutionError(err)
ret = {}
pkg_re = re.compile(r'(\S+)\s+(\S+)')
pkg_re = re.compile(r'(\S+)\|(\S+)')
for line in result['stdout'].split('\n'):
if line.startswith("No packages"):
return ret

View File

@ -5887,11 +5887,19 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base',
.. versionadded:: 2016.11.0
'''
create_kwargs = salt.utils.clean_kwargs(**copy.deepcopy(kwargs))
for key in ('image', 'name', 'cmd', 'interactive', 'tty'):
try:
del create_kwargs[key]
except KeyError:
pass
# start a new container
ret = __salt__['dockerng.create'](image=base,
name=name,
cmd='sleep infinity',
interactive=True, tty=True)
interactive=True, tty=True,
**create_kwargs)
id_ = ret['Id']
try:
__salt__['dockerng.start'](id_)

View File

@ -182,7 +182,7 @@ def send_msg_multi(message,
password = creds.get('xmpp.password')
xmpp = SendMsgBot.create_multi(
jid, password, message, recipients=recipients, rooms=rooms)
jid, password, message, recipients=recipients, rooms=rooms, nick=nick)
if rooms:
xmpp.register_plugin('xep_0045') # MUC plugin

View File

@ -1906,7 +1906,7 @@ class State(object):
Check if the low data chunk should send a failhard signal
'''
tag = _gen_tag(low)
if self.opts['test']:
if self.opts.get('test', False):
return False
if (low.get('failhard', False) or self.opts['failhard']
and tag in running):

View File

@ -141,9 +141,72 @@ def extracted(name,
Ensure that an archive is extracted to a specific directory.
.. important::
**Changes for 2016.11.0**
In earlier releases, this state would rely on the ``if_missing``
argument to determine whether or not the archive needed to be
extracted. When this argument was not passed, then the state would just
assume ``if_missing`` is the same as the ``name`` argument (i.e. the
parent directory into which the archive would be extracted).
This caused a number of annoyances. One such annoyance was the need to
know beforehand a path that would result from the extraction of the
archive, and setting ``if_missing`` to that directory, like so:
.. code-block:: yaml
extract_myapp:
archive.extracted:
- name: /var/www
- source: salt://apps/src/myapp-16.2.4.tar.gz
- user: www
- group: www
- if_missing: /var/www/myapp-16.2.4
If ``/var/www`` already existed, this would effectively make
``if_missing`` a required argument, just to get Salt to extract the
archive.
Some users worked around this by adding the top-level directory of the
archive to the end of the ``name`` argument, and then used ``--strip``
or ``--strip-components`` to remove that top-level dir when extracting:
.. code-block:: yaml
extract_myapp:
archive.extracted:
- name: /var/www/myapp-16.2.4
- source: salt://apps/src/myapp-16.2.4.tar.gz
- user: www
- group: www
- tar_options: --strip-components=1
With the rewrite for 2016.11.0, these workarounds are no longer
necessary. ``if_missing`` is still a supported argument, but it is no
longer required. The equivalent SLS in 2016.11.0 would be:
.. code-block:: yaml
extract_myapp:
archive.extracted:
- name: /var/www
- source: salt://apps/src/myapp-16.2.4.tar.gz
- user: www
- group: www
Salt now uses a function called :py:func:`archive.list
<salt.modules.archive.list>` to get a list of files/directories in the
archive. Using this information, the state can now check the minion to
see if any paths are missing, and know whether or not the archive needs
to be extracted. This makes the ``if_missing`` argument unnecessary in
most use cases.
.. important::
**ZIP Archive Handling**
*Note: this information applies to 2016.11.0 and later.*
Salt has two different functions for extracting ZIP archives:
1. :py:func:`archive.unzip <salt.modules.archive.unzip>`, which uses
@ -183,18 +246,6 @@ def extracted(name,
Therefore, if ``use_cmd_unzip`` is specified and set to ``False``,
and ``options`` is also set, the state will not proceed.
- *Password-protected ZIP Archives* (only supported by
:py:func:`archive.unzip <salt.modules.archive.unzip>`) -
:py:func:`archive.cmd_unzip <salt.modules.archive.cmd_unzip>` is not
be permitted to extract password-protected ZIP archives, as
attempting to do so will cause the unzip command to block on user
input. The :py:func:`archive.is_encrypted
<salt.modules.archive.unzip>` function will be used to determine if
the archive is password-protected. If it is, then the ``password``
argument will be required for the state to proceed. If
``use_cmd_unzip`` is specified and set to ``True``, then the state
will not proceed.
- *Permissions* - Due to an `upstream bug in Python`_, permissions are
not preserved when the zipfile_ module is used to extract an archive.
As of the 2016.11.0 release, :py:func:`archive.unzip
@ -313,6 +364,11 @@ def extracted(name,
**For ZIP archives only.** Password used for extraction.
.. versionadded:: 2016.3.0
.. versionchanged:: 2016.11.0
The newly-added :py:func:`archive.is_encrypted
<salt.modules.archive.is_encrypted>` function will be used to
determine if the archive is password-protected. If it is, then the
``password`` argument will be required for the state to proceed.
options
**For tar and zip archives only.** This option can be used to specify

View File

@ -72,7 +72,7 @@ def installed(name, version=None, source=None, force=False, pre_versions=False,
'comment': ''}
# Determine if the package is installed
if name not in __salt__['cmd.run']('choco list --local-only'):
if name not in __salt__['cmd.run']('choco list --local-only --limit-output'):
ret['changes'] = {'name': '{0} will be installed'.format(name)}
elif force:
ret['changes'] = {'name': '{0} is already installed but will reinstall'
@ -150,7 +150,7 @@ def uninstalled(name, version=None, uninstall_args=None, override_args=False):
'comment': ''}
# Determine if package is installed
if name in __salt__['cmd.run']('choco list --local-only'):
if name in __salt__['cmd.run']('choco list --local-only --limit-output'):
ret['changes'] = {'name': '{0} will be removed'.format(name)}
else:
ret['comment'] = 'The package {0} is not installed'.format(name)

View File

@ -510,7 +510,7 @@ def file(name,
Overrides the default backup mode for the user's crontab.
'''
# Initial set up
mode = salt.utils.normalize_mode('0600')
mode = '0600'
try:
group = __salt__['user.info'](user)['groups'][0]

View File

@ -78,8 +78,7 @@ class AlternativesTestCase(TestCase):
os_readlink_mock.side_effect = OSError('Hell was not found!!!')
self.assertFalse(alternatives.show_current('hell'))
os_readlink_mock.assert_called_with('/etc/alternatives/hell')
self.assertIn('ERROR:alternatives: path /etc/alternatives/hell '
'does not exist',
self.assertIn('ERROR:alternative: hell does not exist',
handler.messages)
@patch('os.readlink')