Merge branch '2016.3' into '2016.11'

Conflicts:
  - salt/cli/batch.py
  - salt/cli/salt.py
  - salt/transport/client.py
  - salt/utils/vmware.py
  - tests/integration/modules/sysmod.py
This commit is contained in:
rallytime 2016-11-14 09:14:00 -07:00
commit 0e332ab591
15 changed files with 140 additions and 37 deletions

View file

@ -12,6 +12,14 @@ Returner Changes
accept a ``minions`` keyword argument. All returners which ship with Salt
have been modified to do so.
New Configuration Parameter: ``rotate_aes_key``
===============================================
- ``Rotate_aes_key`` causes Salt to generate a new AES key whenever a minion key
is deleted. This eliminates the chance that a deleted minion could continue
to eavesdrop on communications with the master if it continues to run after its
key is deleted. See the entry in the documentation for `rotate_aes_key`_.
Ubuntu 16.04 Packages
=====================
@ -640,3 +648,4 @@ Changes:
.. _`#34647`: https://github.com/saltstack/salt/pull/34647
.. _`#34651`: https://github.com/saltstack/salt/pull/34651
.. _`#34676`: https://github.com/saltstack/salt/pull/34676
.. _ `rotate_aes_key`: https://docs.saltstack.com/en/2015.8/ref/configuration/master.html#rotate-aes-key

View file

@ -8,11 +8,18 @@ Version 2016.3.4 is a bugfix release for :doc:`2016.3.0
Known Issues
------------
The Salt Minion does not clean up files in ``/tmp`` when rendering templates. This potentially
results in either running out of disk space or running out of inodes. Please see `Issue #37541`_
for more information. This bug was fixed with `Pull Request #37540`_, which will be available in
the ``2016.3.5`` release of Salt.
The release of the `bootstrap-salt.sh` script that is included with 2016.3.4 release has a bug
in it that fails to install salt correctly for git installs using tags in the 2015.5 branch.
This bug has not been fixed in the `salt-bootstrap repository`_ yet, but the
`previous bootstrap release`_ (v2016.08.16) does not contain this bug.
.. _`Issue #37541`: https://github.com/saltstack/salt/issues/37541
.. _`Pull Request #37540`: https://github.com/saltstack/salt/pull/37540
.. _`previous bootstrap release`: https://raw.githubusercontent.com/saltstack/salt-bootstrap/v2016.08.16/bootstrap-salt.sh
.. _`salt-bootstrap repository`: https://github.com/saltstack/salt-bootstrap

View file

@ -208,9 +208,9 @@ class Batch(object):
break
continue
if self.opts.get('raw'):
parts.update({part['id']: part})
if part['id'] in minion_tracker[queue]['minions']:
minion_tracker[queue]['minions'].remove(part['id'])
parts.update({part['data']['id']: part})
if part['data']['id'] in minion_tracker[queue]['minions']:
minion_tracker[queue]['minions'].remove(part['data']['id'])
else:
print_cli('minion {0} was already deleted from tracker, probably a duplicate key'.format(part['id']))
else:
@ -242,15 +242,11 @@ class Batch(object):
if bwait:
wait.append(datetime.now() + timedelta(seconds=bwait))
if self.opts.get('raw'):
ret[minion] = data
yield data
elif self.opts.get('failhard'):
# When failhard is passed, we need to return all data to include
# the retcode to use in salt/cli/salt.py later. See issue #24996.
else:
ret[minion] = data
yield {minion: data}
else:
ret[minion] = data['ret']
yield {minion: data['ret']}
if not self.quiet:
ret[minion] = data['ret']
data[minion] = data.pop('ret')

View file

@ -51,6 +51,10 @@ class SaltCMD(parsers.SaltCMDOptionParser):
return
if self.options.batch or self.options.static:
# _run_batch() will handle all output and
# exit with the appropriate error condition
# Execution will not continue past this point
# in batch mode.
self._run_batch()
return
@ -239,10 +243,14 @@ class SaltCMD(parsers.SaltCMDOptionParser):
# We will print errors to the console further down the stack
sys.exit(1)
# Printing the output is already taken care of in run() itself
retcode = 0
for res in batch.run():
if self.options.failhard:
for ret in six.itervalues(res):
retcode = self._get_retcode(ret)
for ret in six.itervalues(res):
job_retcode = salt.utils.job.get_retcode(ret)
if job_retcode > retcode:
# Exit with the highest retcode we find
retcode = job_retcode
if self.options.failhard:
if retcode != 0:
sys.stderr.write(
'{0}\nERROR: Minions returned with non-zero exit code.\n'.format(
@ -250,6 +258,7 @@ class SaltCMD(parsers.SaltCMDOptionParser):
)
)
sys.exit(retcode)
sys.exit(retcode)
def _print_errors_summary(self, errors):
if errors:

View file

@ -648,22 +648,34 @@ def list_keypairs(call=None):
)
return False
items = query(method='account/keys')
fetch = True
page = 1
ret = {}
for key_pair in items['ssh_keys']:
name = key_pair['name']
if name in ret:
raise SaltCloudSystemExit(
'A duplicate key pair name, \'{0}\', was found in DigitalOcean\'s '
'key pair list. Please change the key name stored by DigitalOcean. '
'Be sure to adjust the value of \'ssh_key_file\' in your cloud '
'profile or provider configuration, if necessary.'.format(
name
while fetch:
items = query(method='account/keys', command='?page=' + str(page) +
'&per_page=100')
for key_pair in items['ssh_keys']:
name = key_pair['name']
if name in ret:
raise SaltCloudSystemExit(
'A duplicate key pair name, \'{0}\', was found in DigitalOcean\'s '
'key pair list. Please change the key name stored by DigitalOcean. '
'Be sure to adjust the value of \'ssh_key_file\' in your cloud '
'profile or provider configuration, if necessary.'.format(
name
)
)
)
ret[name] = {}
for item in six.iterkeys(key_pair):
ret[name][item] = str(key_pair[item])
ret[name] = {}
for item in six.iterkeys(key_pair):
ret[name][item] = str(key_pair[item])
page += 1
try:
fetch = 'next' in items['links']['pages']
except KeyError:
fetch = False
return ret

View file

@ -87,7 +87,7 @@ import salt.utils
import tempfile
import salt.utils.locales
import salt.utils.url
from salt.ext.six import string_types
from salt.ext.six import string_types, iteritems
from salt.exceptions import CommandExecutionError, CommandNotFoundError
@ -788,6 +788,9 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
if env_vars:
if isinstance(env_vars, dict):
for k, v in iteritems(env_vars):
if not isinstance(v, string_types):
env_vars[k] = str(v)
os.environ.update(env_vars)
else:
raise CommandExecutionError(

View file

@ -125,6 +125,14 @@ The corresponding Pillar top file would look like this:
'*':
- bar
.. note::
This feature was unintentionally omitted when git_pillar was rewritten for
the 2015.8.0 release. It was added again in the 2016.3.4 release, but it
has changed slightly in that release. On Salt masters running 2015.8.0
through 2016.3.3, this feature can only be accessed using the legacy config
described above. For 2016.3.4 and later, refer to explanation of the
``__env__`` parameter in the below section.
.. _git-pillar-2015-8-0-and-later:
Configuring git_pillar for Salt releases 2015.8.0 and later
@ -204,6 +212,36 @@ per-remote parameter:
- production https://gitserver/git-pillar.git:
- env: prod
If ``__env__`` is specified as the branch name, then git_pillar will use the
branch specified by :conf_master:`git_pillar_base`:
.. code-block:: yaml
ext_pillar:
- git:
- __env__ https://gitserver/git-pillar.git:
- root: pillar
The corresponding Pillar top file would look like this:
.. code-block:: yaml
{{env}}:
'*':
- bar
.. note::
This feature was unintentionally omitted when git_pillar was rewritten for
the 2015.8.0 release. It was added again in the 2016.3.4 release, but it
has changed slightly in that release. On Salt masters running 2015.8.0
through 2016.3.3, this feature can only be accessed using the legacy config
in the previous section of this page.
For 2016.3.4 and later, the above example is accurate, and the value
replaced by ``__env__`` is :conf_master:`git_pillar_base`, while the legacy
config's version of this feature replaces ``__env__`` with
:conf_master:`gitfs_base`.
With the addition of pygit2_ support, git_pillar can now interact with
authenticated remotes. Authentication works just like in gitfs (as outlined in
the :ref:`Git Fileserver Backend Walkthrough <gitfs-authentication>`), only

View file

@ -2924,12 +2924,8 @@ class BaseHighState(object):
'''
if not self.opts['autoload_dynamic_modules']:
return
if self.opts.get('local', False):
syncd = self.state.functions['saltutil.sync_all'](list(matches),
refresh=False)
else:
syncd = self.state.functions['saltutil.sync_all'](list(matches),
refresh=False)
syncd = self.state.functions['saltutil.sync_all'](list(matches),
refresh=False)
if syncd['grains']:
self.opts['grains'] = salt.loader.grains(self.opts)
self.state.opts['pillar'] = self.state._gather_pillar()

View file

@ -114,7 +114,7 @@ class AsyncReqChannel(AsyncChannel):
return salt.transport.tcp.AsyncTCPReqChannel(opts, **kwargs)
elif ttype == 'raet':
import salt.transport.raet
return salt.transport.raet.AsyncRAETReqChannel(opts, **kwargs)
return salt.transport.raet.RAETReqChannel(opts, **kwargs)
elif ttype == 'local':
import salt.transport.local
return salt.transport.local.AsyncLocalChannel(opts, **kwargs)

View file

@ -577,8 +577,10 @@ def get_location(opts=None, provider=None):
DEFAULT_LOCATION
'''
if opts is None:
opts = __opts__
ret = opts.get('location', provider.get('location'))
opts = {}
ret = opts.get('location')
if ret is None and provider is not None:
ret = provider.get('location')
if ret is None:
ret = get_region_from_metadata()
if ret is None:

View file

@ -360,7 +360,7 @@ class GroupOption(Option):
self.gids.add(int(name))
else:
try:
self.gids.add(grp.getgrnam(value).gr_gid)
self.gids.add(grp.getgrnam(name).gr_gid)
except KeyError:
raise ValueError('no such group "{0}"'.format(name))

View file

@ -787,6 +787,7 @@ class CkMinions(object):
# so this fn is permitted by all minions
if self.match_check(auth_list_entry, fun):
return True
continue
if isinstance(auth_list_entry, dict):
if len(auth_list_entry) != 1:
log.info('Malformed ACL: {0}'.format(auth_list_entry))

View file

@ -54,6 +54,12 @@ class BatchTest(integration.ShellCase):
self.assertIn(sub_min_ret, cmd)
self.assertIn(min_ret, cmd)
def test_batch_exit_code(self):
'''
Test that a failed state returns a non-zero exit code in batch mode
'''
cmd = self.run_salt(' "*" state.single test.fail_without_changes name=test_me -b 25%', with_retcode=True)
self.assertEqual(cmd[-1], 2)
if __name__ == '__main__':
from integration import run_tests

View file

@ -85,6 +85,29 @@ class StdTest(integration.ModuleCase):
continue
self.assertTrue(ret['minion'])
def test_batch(self):
'''
test cmd_batch
'''
cmd_batch = self.client.cmd_batch(
'minion',
'test.ping',
)
for ret in cmd_batch:
self.assertTrue(ret['minion'])
def test_batch_raw(self):
'''
test cmd_batch with raw option
'''
cmd_batch = self.client.cmd_batch(
'minion',
'test.ping',
raw=True,
)
for ret in cmd_batch:
self.assertTrue(ret['data']['success'])
def test_full_returns(self):
'''
test cmd_iter

View file

@ -45,6 +45,7 @@ class SysModuleTest(integration.ModuleCase):
'nspawn.restart',
'lowpkg.bin_pkg_info',
'state.apply',
'pip.iteritems',
'cmd.win_runas',
'status.list2cmdline'
)