Merge branch '2017.7' into '2018.3'

Conflicts:
  - requirements/base.txt
  - salt/states/archive.py
  - salt/states/file.py
  - tests/integration/runners/test_state.py
  - tests/unit/daemons/test_masterapi.py
This commit is contained in:
rallytime 2018-06-11 11:45:37 -04:00
commit 3d2ea16c3a
No known key found for this signature in database
GPG Key ID: E8F1A4B90D0DEA19
13 changed files with 474 additions and 139 deletions

View File

@ -1,10 +1,6 @@
-r base.txt -r base.txt
mock>=2.0.0 mock>=2.0.0
apache-libcloud>=0.14.0
boto>=2.32.1
boto3>=1.2.1
moto>=0.3.6
SaltPyLint>=v2017.3.6 SaltPyLint>=v2017.3.6
pytest>=3.5.0 pytest>=3.5.0
git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt

36
requirements/tests.txt Normal file
View File

@ -0,0 +1,36 @@
-r zeromq.txt
-r dev.txt
-r pytest.txt
apache-libcloud>=1.0.0
boto>=2.32.1
boto3>=1.2.1
moto>=0.3.6
docker; sys.platform != 'win32'
docker==2.7.0; sys.platform == 'win32'
virtualenv
setuptools>=30
six>=1.10.0
timelib
coverage
keyring==5.7.1
python-gnupg
python-etcd==0.4.2
GitPython
supervisor; python_version < '3'
kubernetes<4.0
psutil
pyvmomi
setproctitle
cherrypy; sys.platform != 'win32' and sys.platform != 'darwin'
pyinotify; sys.platform != 'win32' and sys.platform != 'darwin'
PyMySQL; sys.platform != 'win32' and sys.platform != 'darwin'
jsonschema
strict_rfc3339
rfc3987
jinja2
pyOpenSSL
ioflo
dnspython
SaltTesting==2017.6.1
junos-eznc
jxmlease

View File

@ -66,6 +66,7 @@ def _gen_checksum(path):
def _checksum_file_path(path): def _checksum_file_path(path):
try:
relpath = '.'.join((os.path.relpath(path, __opts__['cachedir']), 'hash')) relpath = '.'.join((os.path.relpath(path, __opts__['cachedir']), 'hash'))
if re.match(r'..[/\\]', relpath): if re.match(r'..[/\\]', relpath):
# path is a local file # path is a local file
@ -73,7 +74,20 @@ def _checksum_file_path(path):
'local', 'local',
os.path.splitdrive(path)[-1].lstrip('/\\'), os.path.splitdrive(path)[-1].lstrip('/\\'),
) )
return salt.utils.path.join(__opts__['cachedir'], 'archive_hash', relpath) except ValueError as exc:
# The path is on a different drive (Windows)
if six.text_type(exc).startswith('path is on'):
drive, path = os.path.splitdrive(path)
relpath = salt.utils.path.join(
'local',
drive.rstrip(':'),
path.lstrip('/\\'),
)
else:
raise
ret = salt.utils.path.join(__opts__['cachedir'], 'archive_hash', relpath)
log.debug('Using checksum file %s for cached archive file %s', ret, path)
return ret
def _update_checksum(path): def _update_checksum(path):

View File

@ -673,13 +673,13 @@ def _error(ret, err_msg):
def _check_directory(name, def _check_directory(name,
user, user=None,
group, group=None,
recurse, recurse=False,
mode, mode=None,
clean, clean=False,
require, require=False,
exclude_pat, exclude_pat=None,
max_depth=None, max_depth=None,
follow_symlinks=False): follow_symlinks=False):
''' '''
@ -764,7 +764,7 @@ def _check_directory(name,
def _check_directory_win(name, def _check_directory_win(name,
win_owner, win_owner=None,
win_perms=None, win_perms=None,
win_deny_perms=None, win_deny_perms=None,
win_inheritance=None, win_inheritance=None,
@ -778,6 +778,7 @@ def _check_directory_win(name,
changes = {name: {'directory': 'new'}} changes = {name: {'directory': 'new'}}
else: else:
# Check owner # Check owner
if win_owner is not None:
owner = salt.utils.win_dacl.get_owner(name) owner = salt.utils.win_dacl.get_owner(name)
if not owner.lower() == win_owner.lower(): if not owner.lower() == win_owner.lower():
changes['owner'] = win_owner changes['owner'] = win_owner
@ -956,33 +957,46 @@ def _check_touch(name, atime, mtime):
def _get_symlink_ownership(path): def _get_symlink_ownership(path):
if salt.utils.is_windows():
owner = salt.utils.win_dacl.get_owner(path)
return owner, owner
else:
return ( return (
__salt__['file.get_user'](path, follow_symlinks=False), __salt__['file.get_user'](path, follow_symlinks=False),
__salt__['file.get_group'](path, follow_symlinks=False) __salt__['file.get_group'](path, follow_symlinks=False)
) )
def _check_symlink_ownership(path, user, group): def _check_symlink_ownership(path, user, group, win_owner):
''' '''
Check if the symlink ownership matches the specified user and group Check if the symlink ownership matches the specified user and group
''' '''
cur_user, cur_group = _get_symlink_ownership(path) cur_user, cur_group = _get_symlink_ownership(path)
if salt.utils.is_windows():
return win_owner == cur_user
else:
return (cur_user == user) and (cur_group == group) return (cur_user == user) and (cur_group == group)
def _set_symlink_ownership(path, user, group): def _set_symlink_ownership(path, user, group, win_owner):
''' '''
Set the ownership of a symlink and return a boolean indicating Set the ownership of a symlink and return a boolean indicating
success/failure success/failure
''' '''
if salt.utils.is_windows():
try:
salt.utils.win_dacl.set_owner(path, win_owner)
except CommandExecutionError:
pass
else:
try: try:
__salt__['file.lchown'](path, user, group) __salt__['file.lchown'](path, user, group)
except OSError: except OSError:
pass pass
return _check_symlink_ownership(path, user, group) return _check_symlink_ownership(path, user, group, win_owner)
def _symlink_check(name, target, force, user, group): def _symlink_check(name, target, force, user, group, win_owner):
''' '''
Check the symlink function Check the symlink function
''' '''
@ -1001,7 +1015,7 @@ def _symlink_check(name, target, force, user, group):
else: else:
result = True result = True
msg = 'The symlink {0} is present'.format(name) msg = 'The symlink {0} is present'.format(name)
if not _check_symlink_ownership(name, user, group): if not _check_symlink_ownership(name, user, group, win_owner):
result = None result = None
pchanges['ownership'] = '{0}:{1}'.format(*_get_symlink_ownership(name)) pchanges['ownership'] = '{0}:{1}'.format(*_get_symlink_ownership(name))
msg += ( msg += (
@ -1225,6 +1239,57 @@ def _shortcut_check(name,
'should be. Did you mean to use force?'.format(name)), pchanges 'should be. Did you mean to use force?'.format(name)), pchanges
def _makedirs(name,
user=None,
group=None,
dir_mode=None,
win_owner=None,
win_perms=None,
win_deny_perms=None,
win_inheritance=None):
'''
Helper function for creating directories when the ``makedirs`` option is set
to ``True``. Handles Unix and Windows based systems
.. versionadded:: 2017.7.7
Args:
name (str): The directory path to create
user (str): The linux user to own the directory
group (str): The linux group to own the directory
dir_mode (str): The linux mode to apply to the directory
win_owner (str): The Windows user to own the directory
win_perms (dict): A dictionary of grant permissions for Windows
win_deny_perms (dict): A dictionary of deny permissions for Windows
win_inheritance (bool): True to inherit permissions on Windows
Returns:
bool: True if successful, otherwise False on Windows
str: Error messages on failure on Linux
None: On successful creation on Linux
Raises:
CommandExecutionError: If the drive is not mounted on Windows
'''
if salt.utils.is_windows():
# Make sure the drive is mapped before trying to create the
# path in windows
drive, path = os.path.splitdrive(name)
if not os.path.isdir(drive):
raise CommandExecutionError(drive)
win_owner = win_owner if win_owner else user
return __salt__['file.makedirs'](path=name,
owner=win_owner,
grant_perms=win_perms,
deny_perms=win_deny_perms,
inheritance=win_inheritance)
else:
return __salt__['file.makedirs'](path=name,
user=user,
group=group,
mode=dir_mode)
def symlink( def symlink(
name, name,
target, target,
@ -1234,6 +1299,10 @@ def symlink(
user=None, user=None,
group=None, group=None,
mode=None, mode=None,
win_owner=None,
win_perms=None,
win_deny_perms=None,
win_inheritance=None,
**kwargs): **kwargs):
''' '''
Create a symbolic link (symlink, soft link) Create a symbolic link (symlink, soft link)
@ -1282,6 +1351,28 @@ def symlink(
The default mode for new files and directories corresponds umask of salt The default mode for new files and directories corresponds umask of salt
process. For existing files and directories it's not enforced. process. For existing files and directories it's not enforced.
win_owner : None
The owner of the symlink and directories if ``makedirs`` is True. If
this is not passed, ``user`` will be used. If ``user`` is not passed,
the account under which Salt is running will be used.
.. versionadded:: 2017.7.7
win_perms : None
A dictionary containing permissions to grant
.. versionadded:: 2017.7.7
win_deny_perms : None
A dictionary containing permissions to deny
.. versionadded:: 2017.7.7
win_inheritance : None
True to inherit permissions from parent, otherwise False
.. versionadded:: 2017.7.7
''' '''
name = os.path.expanduser(name) name = os.path.expanduser(name)
@ -1310,10 +1401,16 @@ def symlink(
if not user: if not user:
user = 'SYSTEM' user = 'SYSTEM'
# If win_owner is not passed, use user
if win_owner is None:
win_owner = user if user else None
# Group isn't relevant to Windows, use win_perms/win_deny_perms
if group is not None: if group is not None:
log.warning( log.warning(
'The group argument for {0} has been ignored as this ' 'The group argument for {0} has been ignored as this '
'is a Windows system.'.format(name) 'is a Windows system. Please use the `win_*` parameters to set '
'permissions in Windows.'.format(name)
) )
group = user group = user
@ -1323,6 +1420,23 @@ def symlink(
) )
preflight_errors = [] preflight_errors = []
if salt.utils.is_windows():
# Make sure the passed owner exists
if not salt.utils.win_functions.get_sid_from_name(win_owner):
preflight_errors.append('User {0} does not exist'.format(win_owner))
# Make sure users passed in win_perms exist
if win_perms:
for name_check in win_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
preflight_errors.append('User {0} does not exist'.format(name_check))
# Make sure users passed in win_deny_perms exist
if win_deny_perms:
for name_check in win_deny_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
preflight_errors.append('User {0} does not exist'.format(name_check))
else:
uid = __salt__['file.user_to_uid'](user) uid = __salt__['file.user_to_uid'](user)
gid = __salt__['file.group_to_gid'](group) gid = __salt__['file.group_to_gid'](group)
@ -1347,19 +1461,29 @@ def symlink(
target, target,
force, force,
user, user,
group) group,
if __opts__['test']: win_owner)
ret['result'] = presult
ret['comment'] = pcomment
return ret
if not os.path.isdir(os.path.dirname(name)): if not os.path.isdir(os.path.dirname(name)):
if makedirs: if makedirs:
__salt__['file.makedirs']( if __opts__['test']:
name, pcomment += '\n{0} will be created'.format(os.path.dirname(name))
else:
try:
_makedirs(name=name,
user=user, user=user,
group=group, group=group,
mode=mode) dir_mode=mode,
win_owner=win_owner,
win_perms=win_perms,
win_deny_perms=win_deny_perms,
win_inheritance=win_inheritance)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else:
if __opts__['test']:
pcomment += '\nDirectory {0} for symlink is not present' \
''.format(os.path.dirname(name))
else: else:
return _error( return _error(
ret, ret,
@ -1367,27 +1491,47 @@ def symlink(
os.path.dirname(name) os.path.dirname(name)
) )
) )
if __opts__['test']:
ret['result'] = presult
ret['comment'] = pcomment
return ret
if __salt__['file.is_link'](name): if __salt__['file.is_link'](name):
# The link exists, verify that it matches the target # The link exists, verify that it matches the target
if os.path.normpath(__salt__['file.readlink'](name)) != os.path.normpath(target): if os.path.normpath(__salt__['file.readlink'](name)) != os.path.normpath(target):
# The target is wrong, delete the link # The target is wrong, delete the link
os.remove(name) os.remove(name)
else: else:
if _check_symlink_ownership(name, user, group): if _check_symlink_ownership(name, user, group, win_owner):
# The link looks good! # The link looks good!
if salt.utils.is_windows():
ret['comment'] = ('Symlink {0} is present and owned by {1}'
''.format(name, win_owner))
else:
ret['comment'] = ('Symlink {0} is present and owned by ' ret['comment'] = ('Symlink {0} is present and owned by '
'{1}:{2}'.format(name, user, group)) '{1}:{2}'.format(name, user, group))
else: else:
if _set_symlink_ownership(name, user, group): if _set_symlink_ownership(name, user, group, win_owner):
if salt.utils.is_windows():
ret['comment'] = ('Set ownership of symlink {0} to '
'{1}'.format(name, win_owner))
ret['changes']['ownership'] = win_owner
else:
ret['comment'] = ('Set ownership of symlink {0} to ' ret['comment'] = ('Set ownership of symlink {0} to '
'{1}:{2}'.format(name, user, group)) '{1}:{2}'.format(name, user, group))
ret['changes']['ownership'] = '{0}:{1}'.format(user, group) ret['changes']['ownership'] = '{0}:{1}'.format(user,
group)
else: else:
ret['result'] = False ret['result'] = False
if salt.utils.is_windows():
ret['comment'] += (
'Failed to set ownership of symlink '
'{0} to {1}'.format(name, win_owner))
else:
ret['comment'] += ( ret['comment'] += (
'Failed to set ownership of symlink {0} to ' 'Failed to set ownership of symlink {0} to '
'{1}:{2}'.format(name, user, group) '{1}:{2}'.format(name, user, group))
)
return ret return ret
elif os.path.isfile(name) or os.path.isdir(name): elif os.path.isfile(name) or os.path.isdir(name):
@ -1434,8 +1578,8 @@ def symlink(
'{1}'.format(name, target)) '{1}'.format(name, target))
ret['changes']['new'] = name ret['changes']['new'] = name
if not _check_symlink_ownership(name, user, group): if not _check_symlink_ownership(name, user, group, win_owner):
if not _set_symlink_ownership(name, user, group): if not _set_symlink_ownership(name, user, group, win_owner):
ret['result'] = False ret['result'] = False
ret['comment'] += (', but was unable to set ownership to ' ret['comment'] += (', but was unable to set ownership to '
'{0}:{1}'.format(user, group)) '{0}:{1}'.format(user, group))
@ -2971,23 +3115,17 @@ def directory(name,
# The parent directory does not exist, create them # The parent directory does not exist, create them
if makedirs: if makedirs:
# Everything's good, create the parent Dirs # Everything's good, create the parent Dirs
if salt.utils.platform.is_windows(): try:
# Make sure the drive is mapped before trying to create the _makedirs(name=name,
# path in windows user=user,
drive, path = os.path.splitdrive(name) group=group,
if not os.path.isdir(drive): dir_mode=dir_mode,
return _error( win_owner=win_owner,
ret, 'Drive {0} is not mapped'.format(drive)) win_perms=win_perms,
__salt__['file.makedirs']( win_deny_perms=win_deny_perms,
path=name, win_inheritance=win_inheritance)
owner=win_owner, except CommandExecutionError as exc:
grant_perms=win_perms, return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
deny_perms=win_deny_perms,
inheritance=win_inheritance,
reset=win_perms_reset)
else:
__salt__['file.makedirs'](name, user=user, group=group,
mode=dir_mode)
else: else:
return _error( return _error(
ret, 'No directory to create {0} in'.format(name)) ret, 'No directory to create {0} in'.format(name))
@ -3182,6 +3320,10 @@ def recurse(name,
maxdepth=None, maxdepth=None,
keep_symlinks=False, keep_symlinks=False,
force_symlinks=False, force_symlinks=False,
win_owner=None,
win_perms=None,
win_deny_perms=None,
win_inheritance=True,
**kwargs): **kwargs):
''' '''
Recurse through a subdirectory on the master and copy said subdirectory Recurse through a subdirectory on the master and copy said subdirectory
@ -3341,6 +3483,28 @@ def recurse(name,
If a file or directory is obstructing symlink creation it will be If a file or directory is obstructing symlink creation it will be
recursively removed so that symlink creation can proceed. This recursively removed so that symlink creation can proceed. This
option is usually not needed except in special circumstances. option is usually not needed except in special circumstances.
win_owner : None
The owner of the symlink and directories if ``makedirs`` is True. If
this is not passed, ``user`` will be used. If ``user`` is not passed,
the account under which Salt is running will be used.
.. versionadded:: 2017.7.7
win_perms : None
A dictionary containing permissions to grant
.. versionadded:: 2017.7.7
win_deny_perms : None
A dictionary containing permissions to deny
.. versionadded:: 2017.7.7
win_inheritance : None
True to inherit permissions from parent, otherwise False
.. versionadded:: 2017.7.7
''' '''
if 'env' in kwargs: if 'env' in kwargs:
# "env" is not supported; Use "saltenv". # "env" is not supported; Use "saltenv".
@ -3439,7 +3603,18 @@ def recurse(name,
return _error( return _error(
ret, 'The path {0} exists and is not a directory'.format(name)) ret, 'The path {0} exists and is not a directory'.format(name))
if not __opts__['test']: if not __opts__['test']:
__salt__['file.makedirs_perms'](name, user, group, dir_mode) if salt.utils.is_windows():
win_owner = win_owner if win_owner else user
__salt__['file.makedirs_perms'](path=name,
owner=win_owner,
grant_perms=win_perms,
deny_perms=win_deny_perms,
inheritance=win_inheritance)
else:
__salt__['file.makedirs_perms'](name=name,
user=user,
group=group,
mode=dir_mode)
def add_comment(path, comment): def add_comment(path, comment):
comments = ret['comment'].setdefault(path, []) comments = ret['comment'].setdefault(path, [])
@ -4821,10 +4996,16 @@ def append(name,
if makedirs is True: if makedirs is True:
dirname = os.path.dirname(name) dirname = os.path.dirname(name)
if not __salt__['file.directory_exists'](dirname): if not __salt__['file.directory_exists'](dirname):
__salt__['file.makedirs'](name) try:
check_res, check_msg, ret['pchanges'] = _check_directory( _makedirs(name=name)
dirname, None, None, False, None, False, False, None except CommandExecutionError as exc:
) return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
if salt.utils.is_windows():
check_res, check_msg, ret['pchanges'] = _check_directory_win(dirname)
else:
check_res, check_msg, ret['pchanges'] = _check_directory(dirname)
if not check_res: if not check_res:
return _error(ret, check_msg) return _error(ret, check_msg)
@ -4935,6 +5116,90 @@ def prepend(name,
The text will not be prepended again if it already exists in the file. You The text will not be prepended again if it already exists in the file. You
may specify a single line of text or a list of lines to append. may specify a single line of text or a list of lines to append.
name
The location of the file to append to.
text
The text to be appended, which can be a single string or a list
of strings.
makedirs
If the file is located in a path without a parent directory,
then the state will fail. If makedirs is set to True, then
the parent directories will be created to facilitate the
creation of the named file. Defaults to False.
source
A single source file to append. This source file can be hosted on either
the salt master server, or on an HTTP or FTP server. Both HTTPS and
HTTP are supported as well as downloading directly from Amazon S3
compatible URLs with both pre-configured and automatic IAM credentials
(see s3.get state documentation). File retrieval from Openstack Swift
object storage is supported via swift://container/object_path URLs
(see swift.get documentation).
For files hosted on the salt file server, if the file is located on
the master in the directory named spam, and is called eggs, the source
string is salt://spam/eggs.
If the file is hosted on an HTTP or FTP server, the source_hash argument
is also required.
source_hash
This can be one of the following:
1. a source hash string
2. the URI of a file that contains source hash strings
The function accepts the first encountered long unbroken alphanumeric
string of correct length as a valid hash, in order from most secure to
least secure:
.. code-block:: text
Type Length
====== ======
sha512 128
sha384 96
sha256 64
sha224 56
sha1 40
md5 32
See the ``source_hash`` parameter description for :mod:`file.managed
<salt.states.file.managed>` function for more details and examples.
template
The named templating engine will be used to render the appended-to file.
Defaults to ``jinja``. The following templates are supported:
- :mod:`cheetah<salt.renderers.cheetah>`
- :mod:`genshi<salt.renderers.genshi>`
- :mod:`jinja<salt.renderers.jinja>`
- :mod:`mako<salt.renderers.mako>`
- :mod:`py<salt.renderers.py>`
- :mod:`wempy<salt.renderers.wempy>`
sources
A list of source files to append. If the files are hosted on an HTTP or
FTP server, the source_hashes argument is also required.
source_hashes
A list of source_hashes corresponding to the sources list specified in
the sources argument.
defaults
Default context passed to the template.
context
Overrides default context variables passed to the template.
ignore_whitespace
.. versionadded:: 2015.8.4
Spaces and Tabs in text are ignored by default, when searching for the
appending content, one space or multiple tabs are the same for salt.
Set this option to ``False`` if you want to change this behavior.
Multi-line example: Multi-line example:
.. code-block:: yaml .. code-block:: yaml
@ -5013,10 +5278,15 @@ def prepend(name,
if makedirs is True: if makedirs is True:
dirname = os.path.dirname(name) dirname = os.path.dirname(name)
if not __salt__['file.directory_exists'](dirname): if not __salt__['file.directory_exists'](dirname):
__salt__['file.makedirs'](name) try:
check_res, check_msg, ret['pchanges'] = _check_directory( _makedirs(name=name)
dirname, None, None, False, None, False, False, None except CommandExecutionError as exc:
) return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
if salt.utils.is_windows():
check_res, check_msg, ret['pchanges'] = _check_directory_win(dirname)
else:
check_res, check_msg, ret['pchanges'] = _check_directory(dirname)
if not check_res: if not check_res:
return _error(ret, check_msg) return _error(ret, check_msg)
@ -5302,7 +5572,10 @@ def touch(name, atime=None, mtime=None, makedirs=False):
return ret return ret
if makedirs: if makedirs:
__salt__['file.makedirs'](name) try:
_makedirs(name=name)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
if not os.path.isdir(os.path.dirname(name)): if not os.path.isdir(os.path.dirname(name)):
return _error( return _error(
ret, 'Directory not present to touch file {0}'.format(name) ret, 'Directory not present to touch file {0}'.format(name)
@ -5499,7 +5772,10 @@ def copy(
dname = os.path.dirname(name) dname = os.path.dirname(name)
if not os.path.isdir(dname): if not os.path.isdir(dname):
if makedirs: if makedirs:
__salt__['file.makedirs'](name) try:
_makedirs(name=name)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else: else:
return _error( return _error(
ret, ret,
@ -5596,7 +5872,10 @@ def rename(name, source, force=False, makedirs=False):
dname = os.path.dirname(name) dname = os.path.dirname(name)
if not os.path.isdir(dname): if not os.path.isdir(dname):
if makedirs: if makedirs:
__salt__['file.makedirs'](name) try:
_makedirs(name=name)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else: else:
return _error( return _error(
ret, ret,
@ -6470,9 +6749,10 @@ def shortcut(
if not os.path.isdir(os.path.dirname(name)): if not os.path.isdir(os.path.dirname(name)):
if makedirs: if makedirs:
__salt__['file.makedirs']( try:
name, _makedirs(name=name, user=user)
user=user) except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else: else:
return _error( return _error(
ret, ret,
@ -6495,7 +6775,10 @@ def shortcut(
time.sleep(1) # wait for asynchronous deletion time.sleep(1) # wait for asynchronous deletion
if not os.path.isdir(os.path.dirname(backupname)): if not os.path.isdir(os.path.dirname(backupname)):
if makedirs: if makedirs:
os.makedirs(backupname) try:
_makedirs(name=backupname)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else: else:
return _error(ret, ( return _error(ret, (
'Directory does not exist for' 'Directory does not exist for'

View File

@ -239,7 +239,8 @@ class WindowsUpdateAgent(object):
# Error codes found at the following site: # Error codes found at the following site:
# https://msdn.microsoft.com/en-us/library/windows/desktop/hh968413(v=vs.85).aspx # https://msdn.microsoft.com/en-us/library/windows/desktop/hh968413(v=vs.85).aspx
# https://technet.microsoft.com/en-us/library/cc720442(v=ws.10).aspx # https://technet.microsoft.com/en-us/library/cc720442(v=ws.10).aspx
fail_codes = {-2145124300: 'Download failed: 0x80240034', fail_codes = {-2145107924: 'WinHTTP Send/Receive failed: 0x8024402C',
-2145124300: 'Download failed: 0x80240034',
-2145124302: 'Invalid search criteria: 0x80240032', -2145124302: 'Invalid search criteria: 0x80240032',
-2145124305: 'Cancelled by policy: 0x8024002F', -2145124305: 'Cancelled by policy: 0x8024002F',
-2145124307: 'Missing source: 0x8024002D', -2145124307: 'Missing source: 0x8024002D',

View File

@ -254,11 +254,13 @@ def pytest_runtest_setup(item):
if destructive_tests_marker is not None: if destructive_tests_marker is not None:
if item.config.getoption('--run-destructive') is False: if item.config.getoption('--run-destructive') is False:
pytest.skip('Destructive tests are disabled') pytest.skip('Destructive tests are disabled')
os.environ['DESTRUCTIVE_TESTS'] = six.text_type(item.config.getoption('--run-destructive'))
expensive_tests_marker = item.get_marker('expensive_test') expensive_tests_marker = item.get_marker('expensive_test')
if expensive_tests_marker is not None: if expensive_tests_marker is not None:
if item.config.getoption('--run-expensive') is False: if item.config.getoption('--run-expensive') is False:
pytest.skip('Expensive tests are disabled') pytest.skip('Expensive tests are disabled')
os.environ['EXPENSIVE_TESTS'] = six.text_type(item.config.getoption('--run-expensive'))
skip_if_not_root_marker = item.get_marker('skip_if_not_root') skip_if_not_root_marker = item.get_marker('skip_if_not_root')
if skip_if_not_root_marker is not None: if skip_if_not_root_marker is not None:

View File

@ -1,6 +1,6 @@
{% set versions = {'18':['05', '03', '01'], '16':['04', '03', '02', '00'], '9':['20']} %} {% set versions = {'18':['05', '03', '01'], '16':['04', '03', '02', '00'], '9':['20']} %}
Zzip: 7zip:
{% for major, subversions in versions.items() %} {% for major, subversions in versions.items() %}
{% for minor in subversions %} {% for minor in subversions %}
'{{major}}.{{minor}}.00.0': '{{major}}.{{minor}}.00.0':

View File

@ -126,6 +126,10 @@ class ServiceModuleTest(ModuleCase):
if tuple(self.run_function('grains.item', ['osrelease_info'])['osrelease_info']) == (14, 0o4) and not systemd: if tuple(self.run_function('grains.item', ['osrelease_info'])['osrelease_info']) == (14, 0o4) and not systemd:
# currently upstart does not have a mechanism to report if disabling a service fails if does not exist # currently upstart does not have a mechanism to report if disabling a service fails if does not exist
self.assertTrue(self.run_function('service.disable', [srv_name])) self.assertTrue(self.run_function('service.disable', [srv_name]))
else:
if salt.utils.is_windows():
disable = self.run_function('service.disable', [srv_name])
self.assertTrue('error' in disable.lower())
else: else:
self.assertFalse(self.run_function('service.disable', [srv_name])) self.assertFalse(self.run_function('service.disable', [srv_name]))

View File

@ -270,11 +270,13 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
except AssertionError: except AssertionError:
self.assertSaltTrueReturn(self.run_state('pkg.removed', name=None, pkgs=pkg_targets)) self.assertSaltTrueReturn(self.run_state('pkg.removed', name=None, pkgs=pkg_targets))
try:
ret = self.run_state('pkg.installed', ret = self.run_state('pkg.installed',
name=None, name=None,
pkgs=pkg_targets, pkgs=pkg_targets,
refresh=False) refresh=False)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
finally:
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets) ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
@ -318,11 +320,13 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
pkgs = [{pkg_targets[0]: version}, pkg_targets[1]] pkgs = [{pkg_targets[0]: version}, pkg_targets[1]]
try:
ret = self.run_state('pkg.installed', ret = self.run_state('pkg.installed',
name=None, name=None,
pkgs=pkgs, pkgs=pkgs,
refresh=False) refresh=False)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
finally:
ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets) ret = self.run_state('pkg.removed', name=None, pkgs=pkg_targets)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)

View File

@ -9,7 +9,6 @@ on a single system to test scale capabilities
# Import Python Libs # Import Python Libs
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function
import os import os
import pwd
import time import time
import signal import signal
import optparse import optparse
@ -29,6 +28,7 @@ import salt.utils.yaml
# Import third party libs # Import third party libs
from salt.ext import six from salt.ext import six
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
import tests.support.helpers
OSES = [ OSES = [
@ -148,7 +148,7 @@ def parse():
'-c', '--config-dir', default='', '-c', '--config-dir', default='',
help=('Pass in a configuration directory containing base configuration.') help=('Pass in a configuration directory containing base configuration.')
) )
parser.add_option('-u', '--user', default=pwd.getpwuid(os.getuid()).pw_name) parser.add_option('-u', '--user', default=tests.support.helpers.this_user())
options, _args = parser.parse_args() options, _args = parser.parse_args()

View File

@ -3,6 +3,7 @@
# Import Python libs # Import Python libs
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
from functools import wraps from functools import wraps
import os
import io import io
import stat import stat
@ -12,6 +13,7 @@ import salt.daemons.masterapi as masterapi
import salt.utils.platform import salt.utils.platform
# Import Salt Testing Libs # Import Salt Testing Libs
from tests.support.paths import TMP_CONF_DIR
from tests.support.unit import TestCase, skipIf from tests.support.unit import TestCase, skipIf
from tests.support.mock import ( from tests.support.mock import (
patch, patch,
@ -568,7 +570,7 @@ class RemoteFuncsTestCase(TestCase):
''' '''
def setUp(self): def setUp(self):
opts = salt.config.master_config(None) opts = salt.config.master_config(os.path.join(TMP_CONF_DIR, 'master'))
self.funcs = masterapi.RemoteFuncs(opts) self.funcs = masterapi.RemoteFuncs(opts)
self.funcs.cache = FakeCache() self.funcs.cache = FakeCache()

View File

@ -59,9 +59,11 @@ class DocTestCase(TestCase):
key, val = regex.split(line, 1) key, val = regex.split(line, 1)
# Don't test man pages, this file, # Don't test man pages, this file,
# the page that documents to not use ":doc:", or # the tox virtualenv files, the page
# the doc/conf.py file # that documents to not use ":doc:",
# or the doc/conf.py file
if 'man' in key \ if 'man' in key \
or '.tox/' in key \
or key.endswith('test_doc.py') \ or key.endswith('test_doc.py') \
or key.endswith(os.sep.join(['doc', 'conf.py'])) \ or key.endswith(os.sep.join(['doc', 'conf.py'])) \
or key.endswith(os.sep.join(['conventions', 'documentation.rst'])) \ or key.endswith(os.sep.join(['conventions', 'documentation.rst'])) \

15
tox.ini
View File

@ -2,15 +2,6 @@
envlist = py27,py34,py35,py36 envlist = py27,py34,py35,py36
[testenv] [testenv]
sitepackages = True deps = -r{toxinidir}/requirements/tests.txt
deps = commands = pytest --rootdir {toxinidir} {posargs:--no-print-logs --run-destructive}
py27,pylint: -r{toxinidir}/requirements/dev_python27.txt passenv = LANG HOME
py34,py35,py36: -r{toxinidir}/requirements/dev_python34.txt
commands =
py27: python2 {toxinidir}/tests/runtests.py {posargs:-v --run-destructive}
py34,py35,py36: python3 {toxinidir}/tests/runtests.py {posargs:-v --run-destructive}
[testenv:pylint]
basepython = python2.7
commands = pylint --rcfile={toxinidir}/.testing.pylintrc --disable=W1307 {posargs:setup.py salt/}
pylint --rcfile={toxinidir}/.testing.pylintrc --disable=W0232,E1002,W1307 {posargs:tests/}