Merge branch '2017.7' into 'develop'

Conflicts:
  - salt/cli/cp.py
  - salt/modules/testinframod.py
  - salt/states/jenkins.py
  - tests/unit/utils/test_find.py
This commit is contained in:
rallytime 2017-08-22 09:07:26 -04:00
commit 67cdfd28ad
28 changed files with 1027 additions and 955 deletions

View file

@ -39,6 +39,13 @@ specified target expression.
desitination will be assumed to be a directory. Finally, recursion is now
supported, allowing for entire directories to be copied.
.. versionchanged:: 2016.11.7,2017.7.2
Reverted back to the old copy mode to preserve backward compatibility. The
new functionality added in 2016.6.6 and 2017.7.0 is now available using the
``-C`` or ``--chunked`` CLI arguments. Note that compression, recursive
copying, and support for copying large files is only available in chunked
mode.
Options
=======
@ -56,9 +63,16 @@ Options
.. include:: _includes/target-selection.rst
.. option:: -C, --chunked
Use new chunked mode to copy files. This mode supports large files, recursive
directories copying and compression.
.. versionadded:: 2016.11.7,2017.7.2
.. option:: -n, --no-compression
Disable gzip compression.
Disable gzip compression in chunked mode.
.. versionadded:: 2016.3.7,2016.11.6,2017.7.0

View file

@ -4,23 +4,12 @@ Salt 2016.3.7 Release Notes
Version 2016.3.7 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
controls whether a minion can request that the master revoke its key. When True, a minion
can request a key revocation and the master will comply. If it is False, the key will not
be revoked by the msater.
Changes for v2016.3.6..v2016.3.7
--------------------------------
New master configuration option `require_minion_sign_messages`
This requires that minions cryptographically sign the messages they
publish to the master. If minions are not signing, then log this information
at loglevel 'INFO' and drop the message without acting on it.
Security Fix
============
New master configuration option `drop_messages_signature_fail`
Drop messages from minions when their signatures do not validate.
Note that when this option is False but `require_minion_sign_messages` is True
minions MUST sign their messages but the validity of their signatures
is ignored.
CVE-2017-12791 Maliciously crafted minion IDs can cause unwanted directory traversals on the Salt-master
New minion configuration option `minion_sign_messages`
Causes the minion to cryptographically sign the payload of messages it places
on the event bus for the master. The payloads are signed with the minion's
private key so the master can verify the signature with its public key.
Correct a flaw in minion id validation which could allow certain minions to authenticate to a master despite not having the correct credentials. To exploit the vulnerability, an attacker must create a salt-minion with an ID containing characters that will cause a directory traversal. Credit for discovering the security flaw goes to: Vernhk@qq.com

View file

@ -0,0 +1,29 @@
===========================
Salt 2016.3.8 Release Notes
===========================
Version 2016.3.8 is a bugfix release for :ref:`2016.3.0 <release-2016-3-0>`.
Changes for v2016.3.7..v2016.3.8
--------------------------------
New master configuration option `allow_minion_key_revoke`, defaults to True. This option
controls whether a minion can request that the master revoke its key. When True, a minion
can request a key revocation and the master will comply. If it is False, the key will not
be revoked by the msater.
New master configuration option `require_minion_sign_messages`
This requires that minions cryptographically sign the messages they
publish to the master. If minions are not signing, then log this information
at loglevel 'INFO' and drop the message without acting on it.
New master configuration option `drop_messages_signature_fail`
Drop messages from minions when their signatures do not validate.
Note that when this option is False but `require_minion_sign_messages` is True
minions MUST sign their messages but the validity of their signatures
is ignored.
New minion configuration option `minion_sign_messages`
Causes the minion to cryptographically sign the payload of messages it places
on the event bus for the master. The payloads are signed with the minion's
private key so the master can verify the signature with its public key.

View file

@ -224,7 +224,7 @@ class Cache(object):
fun = '{0}.flush'.format(self.driver)
return self.modules[fun](bank, key=key, **self._kwargs)
def ls(self, bank):
def list(self, bank):
'''
Lists entries stored in the specified bank.
@ -240,11 +240,9 @@ class Cache(object):
Raises an exception if cache driver detected an error accessing data
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.ls'.format(self.driver)
fun = '{0}.list'.format(self.driver)
return self.modules[fun](bank, **self._kwargs)
list = ls
def contains(self, bank, key=None):
'''
Checks if the specified bank contains the specified key.

View file

@ -61,7 +61,7 @@ api = None
# Define the module's virtual name
__virtualname__ = 'consul'
__func_alias__ = {'list': 'ls'}
__func_alias__ = {'list_': 'list'}
def __virtual__():
@ -139,7 +139,7 @@ def flush(bank, key=None):
)
def ls(bank):
def list_(bank):
'''
Return an iterable object containing all entries stored in the specified bank.
'''

View file

@ -24,7 +24,7 @@ import salt.utils.files
log = logging.getLogger(__name__)
__func_alias__ = {'list': 'ls'}
__func_alias__ = {'list_': 'list'}
def __cachedir(kwargs=None):
@ -144,7 +144,7 @@ def flush(bank, key=None, cachedir=None):
return True
def ls(bank, cachedir):
def list_(bank, cachedir):
'''
Return an iterable object containing all entries stored in the specified bank.
'''

View file

@ -161,9 +161,7 @@ from salt.exceptions import SaltCacheError
# -----------------------------------------------------------------------------
__virtualname__ = 'redis'
__func_alias__ = {
'ls': 'list'
}
__func_alias__ = {'list_': 'list'}
log = logging.getLogger(__file__)
@ -478,7 +476,7 @@ def flush(bank, key=None):
return True
def ls(bank):
def list_(bank):
'''
Lists entries stored in the specified bank.
'''

View file

@ -18,14 +18,15 @@ import sys
# Import salt libs
import salt.client
import salt.output
import salt.utils
import salt.utils.gzip_util
import salt.utils.itertools
import salt.utils.minions
import salt.utils.parsers
import salt.utils.platform
import salt.utils.stringutils
from salt.utils.verify import verify_log
import salt.output
import salt.utils.verify
# Import 3rd party libs
from salt.ext import six
@ -46,7 +47,7 @@ class SaltCPCli(salt.utils.parsers.SaltCPOptionParser):
# Setup file logging!
self.setup_logfile_logger()
verify_log(self.config)
salt.utils.verify.verify_log(self.config)
cp_ = SaltCP(self.config)
cp_.run()
@ -103,10 +104,70 @@ class SaltCP(object):
empty_dirs.update(empty_dirs_)
return files, sorted(empty_dirs)
def _file_dict(self, fn_):
'''
Take a path and return the contents of the file as a string
'''
if not os.path.isfile(fn_):
err = 'The referenced file, {0} is not available.'.format(fn_)
sys.stderr.write(err + '\n')
sys.exit(42)
with salt.utils.fopen(fn_, 'r') as fp_:
data = fp_.read()
return {fn_: data}
def _load_files(self):
'''
Parse the files indicated in opts['src'] and load them into a python
object for transport
'''
files = {}
for fn_ in self.opts['src']:
if os.path.isfile(fn_):
files.update(self._file_dict(fn_))
elif os.path.isdir(fn_):
salt.utils.print_cli(fn_ + ' is a directory, only files are supported '
'in non-chunked mode. Use "--chunked" command '
'line argument.')
sys.exit(1)
return files
def run(self):
'''
Make the salt client call
'''
if self.opts['chunked']:
ret = self.run_chunked()
else:
ret = self.run_oldstyle()
salt.output.display_output(
ret,
self.opts.get('output', 'nested'),
self.opts)
def run_oldstyle(self):
'''
Make the salt client call in old-style all-in-one call method
'''
arg = [self._load_files(), self.opts['dest']]
local = salt.client.get_local_client(self.opts['conf_file'])
args = [self.opts['tgt'],
'cp.recv',
arg,
self.opts['timeout'],
]
selected_target_option = self.opts.get('selected_target_option', None)
if selected_target_option is not None:
args.append(selected_target_option)
return local.cmd(*args)
def run_chunked(self):
'''
Make the salt client call in the new fasion chunked multi-call way
'''
files, empty_dirs = self._list_files()
dest = self.opts['dest']
gzip = self.opts['gzip']
@ -168,7 +229,7 @@ class SaltCP(object):
)
args = [
tgt,
'cp.recv',
'cp.recv_chunked',
[remote_path, chunk, append, gzip, mode],
timeout,
]
@ -214,14 +275,11 @@ class SaltCP(object):
else '',
tgt,
)
args = [tgt, 'cp.recv', [remote_path, None], timeout]
args = [tgt, 'cp.recv_chunked', [remote_path, None], timeout]
if selected_target_option is not None:
args.append(selected_target_option)
for minion_id, minion_ret in six.iteritems(local.cmd(*args)):
ret.setdefault(minion_id, {})[remote_path] = minion_ret
salt.output.display_output(
ret,
self.opts.get('output', 'nested'),
self.opts)
return ret

View file

@ -3432,34 +3432,7 @@ def list_nodes_full(location=None, call=None):
'or --function.'
)
if not location:
ret = {}
locations = set(
get_location(vm_) for vm_ in six.itervalues(__opts__['profiles'])
if _vm_provider_driver(vm_)
)
# If there aren't any profiles defined for EC2, check
# the provider config file, or use the default location.
if not locations:
locations = [get_location()]
for loc in locations:
ret.update(_list_nodes_full(loc))
return ret
return _list_nodes_full(location)
def _vm_provider_driver(vm_):
alias, driver = vm_['driver'].split(':')
if alias not in __opts__['providers']:
return None
if driver not in __opts__['providers'][alias]:
return None
return driver == 'ec2'
return _list_nodes_full(location or get_location())
def _extract_name_tag(item):

File diff suppressed because it is too large Load diff

View file

@ -503,7 +503,7 @@ class Key(object):
if minion not in minions and minion not in preserve_minions:
shutil.rmtree(os.path.join(m_cache, minion))
cache = salt.cache.factory(self.opts)
clist = cache.ls(self.ACC)
clist = cache.list(self.ACC)
if clist:
for minion in clist:
if minion not in minions and minion not in preserve_minions:
@ -981,7 +981,7 @@ class RaetKey(Key):
if minion not in minions:
shutil.rmtree(os.path.join(m_cache, minion))
cache = salt.cache.factory(self.opts)
clist = cache.ls(self.ACC)
clist = cache.list(self.ACC)
if clist:
for minion in clist:
if minion not in minions and minion not in preserve_minions:

View file

@ -60,7 +60,36 @@ def _gather_pillar(pillarenv, pillar_override):
return ret
def recv(dest, chunk, append=False, compressed=True, mode=None):
def recv(files, dest):
'''
Used with salt-cp, pass the files dict, and the destination.
This function receives small fast copy files from the master via salt-cp.
It does not work via the CLI.
'''
ret = {}
for path, data in six.iteritems(files):
if os.path.basename(path) == os.path.basename(dest) \
and not os.path.isdir(dest):
final = dest
elif os.path.isdir(dest):
final = os.path.join(dest, os.path.basename(path))
elif os.path.isdir(os.path.dirname(dest)):
final = dest
else:
return 'Destination unavailable'
try:
with salt.utils.fopen(final, 'w+') as fp_:
fp_.write(data)
ret[final] = True
except IOError:
ret[final] = False
return ret
def recv_chunked(dest, chunk, append=False, compressed=True, mode=None):
'''
This function receives files copied to the minion using ``salt-cp`` and is
not intended to be used directly on the CLI.

View file

@ -801,7 +801,7 @@ def get_client_args():
salt myminion docker.get_client_args
'''
return salt.utils.docker.get_client_args()
return __utils__['docker.get_client_args']()
def _get_create_kwargs(image,

View file

@ -37,7 +37,7 @@ import salt.utils.files
# Import 3rd-party libs
# pylint: disable=import-error,no-name-in-module,redefined-builtin
from salt.exceptions import SaltInvocationError
from salt.exceptions import CommandExecutionError, SaltInvocationError
# pylint: enable=import-error,no-name-in-module
log = logging.getLogger(__name__)
@ -89,9 +89,22 @@ def _connect():
password=jenkins_password)
def _retrieve_config_xml(config_xml, saltenv):
'''
Helper to cache the config XML and raise a CommandExecutionError if we fail
to do so. If we successfully cache the file, return the cached path.
'''
ret = __salt__['cp.cache_file'](config_xml, saltenv)
if not ret:
raise CommandExecutionError('Failed to retrieve {0}'.format(config_xml))
return ret
def run(script):
'''
.. versionadded:: Carbon
.. versionadded:: 2017.7.0
Execute a groovy script on the jenkins master
@ -166,7 +179,7 @@ def job_exists(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if server.job_exists(name):
@ -190,12 +203,12 @@ def get_job_info(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exist.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
job_info = server.get_job_info(name)
if job_info:
@ -219,17 +232,19 @@ def build_job(name=None, parameters=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exist.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist.'.format(name))
try:
server.build_job(name, parameters)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error building job \'{0}\': {1}'.format(name, err)
)
return True
@ -254,15 +269,15 @@ def create_job(name=None,
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
if job_exists(name):
raise SaltInvocationError('Job `{0}` already exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' already exists'.format(name))
if not config_xml:
config_xml = jenkins.EMPTY_CONFIG_XML
else:
config_xml_file = __salt__['cp.cache_file'](config_xml, saltenv)
config_xml_file = _retrieve_config_xml(config_xml, saltenv)
with salt.utils.files.fopen(config_xml_file) as _fp:
config_xml = _fp.read()
@ -271,7 +286,9 @@ def create_job(name=None,
try:
server.create_job(name, config_xml)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error creating job \'{0}\': {1}'.format(name, err)
)
return config_xml
@ -296,12 +313,12 @@ def update_job(name=None,
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
if not config_xml:
config_xml = jenkins.EMPTY_CONFIG_XML
else:
config_xml_file = __salt__['cp.cache_file'](config_xml, saltenv)
config_xml_file = _retrieve_config_xml(config_xml, saltenv)
with salt.utils.files.fopen(config_xml_file) as _fp:
config_xml = _fp.read()
@ -310,7 +327,9 @@ def update_job(name=None,
try:
server.reconfig_job(name, config_xml)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error updating job \'{0}\': {1}'.format(name, err)
)
return config_xml
@ -329,17 +348,19 @@ def delete_job(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
try:
server.delete_job(name)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error deleting job \'{0}\': {1}'.format(name, err)
)
return True
@ -358,17 +379,19 @@ def enable_job(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
try:
server.enable_job(name)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error enabling job \'{0}\': {1}'.format(name, err)
)
return True
@ -388,17 +411,19 @@ def disable_job(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
try:
server.disable_job(name)
except jenkins.JenkinsException as err:
raise SaltInvocationError('Something went wrong {0}.'.format(err))
raise CommandExecutionError(
'Encountered error disabling job \'{0}\': {1}'.format(name, err)
)
return True
@ -418,12 +443,12 @@ def job_status(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
return server.get_job_info('empty')['buildable']
@ -444,12 +469,12 @@ def get_job_config(name=None):
'''
if not name:
raise SaltInvocationError('Required parameter `name` is missing.')
raise SaltInvocationError('Required parameter \'name\' is missing')
server = _connect()
if not job_exists(name):
raise SaltInvocationError('Job `{0}` does not exists.'.format(name))
raise CommandExecutionError('Job \'{0}\' does not exist'.format(name))
job_info = server.get_job_config(name)
return job_info

View file

@ -51,20 +51,18 @@ try:
import kubernetes.client
from kubernetes.client.rest import ApiException
from urllib3.exceptions import HTTPError
try:
# There is an API change in Kubernetes >= 2.0.0.
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
except ImportError:
from kubernetes.client import AppsV1beta1Deployment
from kubernetes.client import AppsV1beta1DeploymentSpec
HAS_LIBS = True
except ImportError:
HAS_LIBS = False
try:
# There is an API change in Kubernetes >= 2.0.0.
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
except ImportError:
from kubernetes.client import AppsV1beta1Deployment
from kubernetes.client import AppsV1beta1DeploymentSpec
log = logging.getLogger(__name__)
__virtualname__ = 'kubernetes'

View file

@ -37,7 +37,7 @@ def _table_attrs(table):
'''
Helper function to find valid table attributes
'''
cmd = 'osqueryi --json "pragma table_info({0})"'.format(table)
cmd = ['osqueryi'] + ['--json'] + ['pragma table_info{0}'.format(table)]
res = __salt__['cmd.run_all'](cmd)
if res['retcode'] == 0:
attrs = []
@ -56,7 +56,7 @@ def _osquery(sql, format='json'):
'result': True,
}
cmd = 'osqueryi --json "{0}"'.format(sql)
cmd = ['osqueryi'] + ['--json'] + [sql]
res = __salt__['cmd.run_all'](cmd)
if res['stderr']:
ret['result'] = False

View file

@ -15,6 +15,7 @@ import logging
# Import Salt libs
from salt.ext import six
from salt.ext.six.moves import zip
from salt.exceptions import CommandExecutionError
import salt.utils.files
# Import XML parser
@ -36,17 +37,23 @@ def _elements_equal(e1, e2):
return all(_elements_equal(c1, c2) for c1, c2 in zip(e1, e2))
def _fail(ret, msg):
ret['comment'] = msg
ret['result'] = False
return ret
def present(name,
config=None,
**kwargs):
'''
Ensure the job is present in the Jenkins
configured jobs
Ensure the job is present in the Jenkins configured jobs
name
The unique name for the Jenkins job
config
The Salt URL for the file to use for
configuring the job.
The Salt URL for the file to use for configuring the job
'''
ret = {'name': name,
@ -54,9 +61,7 @@ def present(name,
'changes': {},
'comment': ['Job {0} is up to date.'.format(name)]}
_job_exists = __salt__['jenkins.job_exists'](name)
if _job_exists:
if __salt__['jenkins.job_exists'](name):
_current_job_config = __salt__['jenkins.get_job_config'](name)
buf = six.moves.StringIO(_current_job_config)
oldXML = ET.fromstring(buf.read())
@ -68,21 +73,28 @@ def present(name,
diff = difflib.unified_diff(
ET.tostringlist(oldXML, encoding='utf8', method='xml'),
ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='')
__salt__['jenkins.update_job'](name, config, __env__)
ret['changes'][name] = ''.join(diff)
ret['comment'].append('Job {0} updated.'.format(name))
try:
__salt__['jenkins.update_job'](name, config, __env__)
except CommandExecutionError as exc:
return _fail(ret, exc.strerror)
else:
ret['changes'] = ''.join(diff)
ret['comment'].append('Job \'{0}\' updated.'.format(name))
else:
cached_source_path = __salt__['cp.cache_file'](config, __env__)
with salt.utils.files.fopen(cached_source_path) as _fp:
new_config_xml = _fp.read()
__salt__['jenkins.create_job'](name, config, __env__)
try:
__salt__['jenkins.create_job'](name, config, __env__)
except CommandExecutionError as exc:
return _fail(ret, exc.strerror)
buf = six.moves.StringIO(new_config_xml)
diff = difflib.unified_diff('', buf.readlines(), lineterm='')
ret['changes'][name] = ''.join(diff)
ret['comment'].append('Job {0} added.'.format(name))
ret['comment'].append('Job \'{0}\' added.'.format(name))
ret['comment'] = '\n'.join(ret['comment'])
return ret
@ -91,24 +103,23 @@ def present(name,
def absent(name,
**kwargs):
'''
Ensure the job is present in the Jenkins
configured jobs
Ensure the job is absent from the Jenkins configured jobs
name
The name of the Jenkins job to remove.
The name of the Jenkins job to remove
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': []}
_job_exists = __salt__['jenkins.job_exists'](name)
if _job_exists:
__salt__['jenkins.delete_job'](name)
ret['comment'] = 'Job {0} deleted.'.format(name)
if __salt__['jenkins.job_exists'](name):
try:
__salt__['jenkins.delete_job'](name)
except CommandExecutionError as exc:
return _fail(ret, exc.strerror)
else:
ret['comment'] = 'Job \'{0}\' deleted.'.format(name)
else:
ret['comment'] = 'Job {0} already absent.'.format(name)
ret['comment'] = 'Job \'{0}\' already absent.'.format(name)
return ret

View file

@ -72,7 +72,7 @@ class ThorState(salt.state.HighState):
cache = {'grains': {}, 'pillar': {}}
if self.grains or self.pillar:
if self.opts.get('minion_data_cache'):
minions = self.cache.ls('minions')
minions = self.cache.list('minions')
if not minions:
return cache
for minion in minions:

View file

@ -124,7 +124,7 @@ class MasterPillarUtil(object):
'and enfore_mine_cache are both disabled.')
return mine_data
if not minion_ids:
minion_ids = self.cache.ls('minions')
minion_ids = self.cache.list('minions')
for minion_id in minion_ids:
if not salt.utils.verify.valid_id(self.opts, minion_id):
continue
@ -143,7 +143,7 @@ class MasterPillarUtil(object):
'enabled.')
return grains, pillars
if not minion_ids:
minion_ids = self.cache.ls('minions')
minion_ids = self.cache.list('minions')
for minion_id in minion_ids:
if not salt.utils.verify.valid_id(self.opts, minion_id):
continue
@ -366,7 +366,7 @@ class MasterPillarUtil(object):
# in the same file, 'data.p'
grains, pillars = self._get_cached_minion_data(*minion_ids)
try:
c_minions = self.cache.ls('minions')
c_minions = self.cache.list('minions')
for minion_id in minion_ids:
if not salt.utils.verify.valid_id(self.opts, minion_id):
continue

View file

@ -77,7 +77,7 @@ def get_minion_data(minion, opts):
if opts.get('minion_data_cache', False):
cache = salt.cache.factory(opts)
if minion is None:
for id_ in cache.ls('minions'):
for id_ in cache.list('minions'):
data = cache.fetch('minions/{0}'.format(id_), 'data')
if data is None:
continue
@ -344,13 +344,13 @@ class CkMinions(object):
if greedy:
minions = self._pki_minions()
elif cache_enabled:
minions = self.cache.ls('minions')
minions = self.cache.list('minions')
else:
return []
if cache_enabled:
if greedy:
cminions = self.cache.ls('minions')
cminions = self.cache.list('minions')
else:
cminions = minions
if cminions is None:
@ -414,7 +414,7 @@ class CkMinions(object):
mlist.append(fn_)
return mlist
elif cache_enabled:
return self.cache.ls('minions')
return self.cache.list('minions')
else:
return list()
@ -576,7 +576,7 @@ class CkMinions(object):
'''
minions = set()
if self.opts.get('minion_data_cache', False):
search = self.cache.ls('minions')
search = self.cache.list('minions')
if search is None:
return minions
addrs = salt.utils.network.local_port_tcp(int(self.opts['publish_port']))

View file

@ -2199,10 +2199,18 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
def _mixin_setup(self):
file_opts_group = optparse.OptionGroup(self, 'File Options')
file_opts_group.add_option(
'-C', '--chunked',
default=False,
dest='chunked',
action='store_true',
help='Use chunked files transfer. Supports big files, recursive '
'lookup and directories creation.'
)
file_opts_group.add_option(
'-n', '--no-compression',
default=True,
dest='compression',
dest='gzip',
action='store_false',
help='Disable gzip compression.'
)
@ -2223,7 +2231,6 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
self.config['tgt'] = self.args[0]
self.config['src'] = [os.path.realpath(x) for x in self.args[1:-1]]
self.config['dest'] = self.args[-1]
self.config['gzip'] = True
def setup_config(self):
return config.master_config(self.get_config_file_path())

View file

@ -51,6 +51,7 @@ def get_invalid_docs():
allow_failure = (
'cmd.win_runas',
'cp.recv',
'cp.recv_chunked',
'glance.warn_until',
'ipset.long_range',
'libcloud_compute.get_driver',

View file

@ -209,26 +209,26 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin):
with patch('os.remove', MagicMock(side_effect=OSError)):
self.assertRaises(SaltCacheError, localfs.flush, bank='', key='key', cachedir='/var/cache/salt')
# 'ls' function tests: 3
# 'list' function tests: 3
def test_ls_no_base_dir(self):
def test_list_no_base_dir(self):
'''
Tests that the ls function returns an empty list if the bank directory
doesn't exist.
'''
with patch('os.path.isdir', MagicMock(return_value=False)):
self.assertEqual(localfs.ls(bank='', cachedir=''), [])
self.assertEqual(localfs.list_(bank='', cachedir=''), [])
def test_ls_error_raised_no_bank_directory_access(self):
def test_list_error_raised_no_bank_directory_access(self):
'''
Tests that a SaltCacheError is raised when there is a problem accessing the
cache bank directory.
'''
with patch('os.path.isdir', MagicMock(return_value=True)):
with patch('os.listdir', MagicMock(side_effect=OSError)):
self.assertRaises(SaltCacheError, localfs.ls, bank='', cachedir='')
self.assertRaises(SaltCacheError, localfs.list_, bank='', cachedir='')
def test_ls_success(self):
def test_list_success(self):
'''
Tests the return of the ls function containing bank entries.
'''
@ -240,7 +240,7 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin):
# Now test the return of the ls function
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
self.assertEqual(localfs.ls(bank='bank', cachedir=tmp_dir), ['key'])
self.assertEqual(localfs.list_(bank='bank', cachedir=tmp_dir), ['key'])
# 'contains' function tests: 1

View file

@ -264,6 +264,7 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin):
with patch('salt.utils.files.fopen', mock_open(read_data=MOCK_SHELL_FILE)):
self.assertFalse(cmdmod._is_valid_shell('foo'))
@skipIf(salt.utils.is_windows(), 'Do not run on Windows')
def test_os_environment_remains_intact(self):
'''
Make sure the OS environment is not tainted after running a command

View file

@ -136,10 +136,11 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(docker_mod.__salt__,
{'mine.send': mine_send,
'container_resource.run': MagicMock(),
'cp.cache_file': MagicMock(return_value=False),
'docker.get_client_args': client_args_mock}):
with patch.object(docker_mod, '_get_client', client):
command('container', *args)
'cp.cache_file': MagicMock(return_value=False)}):
with patch.dict(docker_mod.__utils__,
{'docker.get_client_args': client_args_mock}):
with patch.object(docker_mod, '_get_client', client):
command('container', *args)
mine_send.assert_called_with('docker.ps', verbose=True, all=True,
host=True)

View file

@ -102,8 +102,9 @@ test:
- name: echo sls_dir={{sls_dir}}
- cwd: /
''', sls='path.to.sls')
self.assertEqual(result['test']['cmd.run'][0]['name'],
'echo sls_dir=path/to')
self.assertEqual(
result['test']['cmd.run'][0]['name'],
'echo sls_dir=path{0}to'.format(os.sep))
def test_states_declared_with_shorthand_no_args(self):
result = self._render_sls('''

View file

@ -13,7 +13,6 @@ from tests.support.unit import skipIf, TestCase
from tests.support.paths import TMP
# Import salt libs
from salt.ext import six
import salt.utils.files
import salt.utils.find
@ -121,19 +120,11 @@ class TestFind(TestCase):
min_size, max_size = salt.utils.find._parse_size('+1m')
self.assertEqual(min_size, 1048576)
# sys.maxint has been removed in Python3. Use maxsize instead.
if six.PY3:
self.assertEqual(max_size, sys.maxsize)
else:
self.assertEqual(max_size, sys.maxint)
self.assertEqual(max_size, sys.maxsize)
min_size, max_size = salt.utils.find._parse_size('+1M')
self.assertEqual(min_size, 1048576)
# sys.maxint has been removed in Python3. Use maxsize instead.
if six.PY3:
self.assertEqual(max_size, sys.maxsize)
else:
self.assertEqual(max_size, sys.maxint)
self.assertEqual(max_size, sys.maxsize)
def test_option_requires(self):
option = salt.utils.find.Option()
@ -217,7 +208,7 @@ class TestFind(TestCase):
option = salt.utils.find.TypeOption('type', 's')
self.assertEqual(option.match('', '', [stat.S_IFSOCK]), True)
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
@skipIf(sys.platform.startswith('win'), 'pwd not available on Windows')
def test_owner_option_requires(self):
self.assertRaises(
ValueError, salt.utils.find.OwnerOption, 'owner', 'notexist'
@ -226,7 +217,7 @@ class TestFind(TestCase):
option = salt.utils.find.OwnerOption('owner', 'root')
self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT)
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
@skipIf(sys.platform.startswith('win'), 'pwd not available on Windows')
def test_owner_option_match(self):
option = salt.utils.find.OwnerOption('owner', 'root')
self.assertEqual(option.match('', '', [0] * 5), True)
@ -234,7 +225,7 @@ class TestFind(TestCase):
option = salt.utils.find.OwnerOption('owner', '500')
self.assertEqual(option.match('', '', [500] * 5), True)
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
@skipIf(sys.platform.startswith('win'), 'grp not available on Windows')
def test_group_option_requires(self):
self.assertRaises(
ValueError, salt.utils.find.GroupOption, 'group', 'notexist'
@ -247,7 +238,7 @@ class TestFind(TestCase):
option = salt.utils.find.GroupOption('group', group_name)
self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT)
@skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows')
@skipIf(sys.platform.startswith('win'), 'grp not available on Windows')
def test_group_option_match(self):
if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')):
group_name = 'wheel'
@ -412,7 +403,7 @@ class TestPrintOption(TestCase):
option.execute('test_name', [0] * 9), [0, 'test_name']
)
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
@skipIf(sys.platform.startswith('win'), "pwd not available on Windows")
def test_print_user(self):
option = salt.utils.find.PrintOption('print', 'user')
self.assertEqual(option.execute('', [0] * 10), 'root')
@ -420,7 +411,7 @@ class TestPrintOption(TestCase):
option = salt.utils.find.PrintOption('print', 'user')
self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31)
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
@skipIf(sys.platform.startswith('win'), "grp not available on Windows")
def test_print_group(self):
option = salt.utils.find.PrintOption('print', 'group')
if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')):
@ -433,7 +424,7 @@ class TestPrintOption(TestCase):
#option = salt.utils.find.PrintOption('print', 'group')
#self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31)
@skipIf(sys.platform.startswith('Windows'), "no /dev/null on windows")
@skipIf(sys.platform.startswith('win'), "no /dev/null on windows")
def test_print_md5(self):
option = salt.utils.find.PrintOption('print', 'md5')
self.assertEqual(option.execute('/dev/null', os.stat('/dev/null')), '')

View file

@ -118,7 +118,7 @@ class NetworkTestCase(TestCase):
(2, 1, 6, '', ('192.30.255.113', 0)),
],
'ipv6host.foo': [
(10, 1, 6, '', ('2001:a71::1', 0, 0, 0)),
(socket.AF_INET6, 1, 6, '', ('2001:a71::1', 0, 0, 0)),
],
}[host]
except KeyError: