mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge branch '2017.7' into fix_46433
This commit is contained in:
commit
89af0a6222
10
doc/conf.py
10
doc/conf.py
@ -250,9 +250,9 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
|
||||
project = 'Salt'
|
||||
|
||||
version = salt.version.__version__
|
||||
latest_release = '2017.7.4' # latest release
|
||||
previous_release = '2016.11.9' # latest release from previous branch
|
||||
previous_release_dir = '2016.11' # path on web server for previous branch
|
||||
latest_release = '2018.3.0' # latest release
|
||||
previous_release = '2017.7.5' # latest release from previous branch
|
||||
previous_release_dir = '2017.7' # path on web server for previous branch
|
||||
next_release = '' # next release
|
||||
next_release_dir = '' # path on web server for next release branch
|
||||
|
||||
@ -263,8 +263,8 @@ if on_saltstack:
|
||||
copyright = time.strftime("%Y")
|
||||
|
||||
# < --- START do not merge these settings to other branches START ---> #
|
||||
build_type = 'latest' # latest, previous, develop, next
|
||||
release = latest_release # version, latest_release, previous_release
|
||||
build_type = 'previous' # latest, previous, develop, next
|
||||
release = previous_release # version, latest_release, previous_release
|
||||
# < --- END do not merge these settings to other branches END ---> #
|
||||
|
||||
# Set google custom search engine
|
||||
|
@ -49,17 +49,13 @@ the SaltStack Repository.
|
||||
Installation from the Community-Maintained Repository
|
||||
=====================================================
|
||||
|
||||
Beginning with version 0.9.4, Salt has been available in `EPEL`_. For
|
||||
RHEL/CentOS 5, `Fedora COPR`_ is a single community repository that provides
|
||||
Salt packages due to the removal from EPEL5.
|
||||
Beginning with version 0.9.4, Salt has been available in `EPEL`_.
|
||||
|
||||
.. note::
|
||||
Packages in these repositories are built by community, and it can
|
||||
take a little while until the latest stable SaltStack release become
|
||||
available.
|
||||
Packages in this repository are built by community, and it can take a little
|
||||
while until the latest stable SaltStack release become available.
|
||||
|
||||
.. _`EPEL`: http://fedoraproject.org/wiki/EPEL
|
||||
.. _`Fedora COPR`: https://copr.fedorainfracloud.org/coprs/saltstack/salt-el5/
|
||||
|
||||
RHEL/CentOS 6 and 7, Scientific Linux, etc.
|
||||
-------------------------------------------
|
||||
@ -146,26 +142,13 @@ ZeroMQ 4
|
||||
========
|
||||
|
||||
We recommend using ZeroMQ 4 where available. SaltStack provides ZeroMQ 4.0.5
|
||||
and pyzmq 14.5.0 in the :ref:`SaltStack Repository <installation-rhel-repo>`
|
||||
as well as a separate `zeromq4 COPR`_ repository.
|
||||
|
||||
.. _`zeromq4 COPR`: http://copr.fedorainfracloud.org/coprs/saltstack/zeromq4/
|
||||
and ``pyzmq`` 14.5.0 in the :ref:`SaltStack Repository
|
||||
<installation-rhel-repo>`.
|
||||
|
||||
If this repository is added *before* Salt is installed, then installing either
|
||||
``salt-master`` or ``salt-minion`` will automatically pull in ZeroMQ 4.0.5, and
|
||||
additional steps to upgrade ZeroMQ and pyzmq are unnecessary.
|
||||
|
||||
.. warning:: RHEL/CentOS 5 Users
|
||||
Using COPR repos on RHEL/CentOS 5 requires that the ``python-hashlib``
|
||||
package be installed. Not having it present will result in checksum errors
|
||||
because YUM will not be able to process the SHA256 checksums used by COPR.
|
||||
|
||||
.. note::
|
||||
For RHEL/CentOS 5 installations, if using the SaltStack repo or Fedora COPR
|
||||
to install Salt (as described :ref:`above <installation-rhel-repo>`),
|
||||
then it is not necessary to enable the `zeromq4 COPR`_, because those
|
||||
repositories already include ZeroMQ 4.
|
||||
|
||||
Package Management
|
||||
==================
|
||||
|
||||
|
@ -37,8 +37,8 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def auth(username, sharedsecret, **kwargs):
|
||||
def auth(username, password):
|
||||
'''
|
||||
Shared secret authentication
|
||||
'''
|
||||
return sharedsecret == __opts__.get('sharedsecret')
|
||||
return password == __opts__.get('sharedsecret')
|
||||
|
@ -2336,6 +2336,9 @@ def wait_for_instance(
|
||||
use_winrm = config.get_cloud_config_value(
|
||||
'use_winrm', vm_, __opts__, default=False
|
||||
)
|
||||
winrm_verify_ssl = config.get_cloud_config_value(
|
||||
'winrm_verify_ssl', vm_, __opts__, default=True
|
||||
)
|
||||
|
||||
if win_passwd and win_passwd == 'auto':
|
||||
log.debug('Waiting for auto-generated Windows EC2 password')
|
||||
@ -2407,7 +2410,8 @@ def wait_for_instance(
|
||||
winrm_port,
|
||||
username,
|
||||
win_passwd,
|
||||
timeout=ssh_connect_timeout):
|
||||
timeout=ssh_connect_timeout,
|
||||
verify=winrm_verify_ssl):
|
||||
raise SaltCloudSystemExit(
|
||||
'Failed to authenticate against remote windows host'
|
||||
)
|
||||
|
110
salt/loader.py
110
salt/loader.py
@ -14,6 +14,7 @@ import logging
|
||||
import inspect
|
||||
import tempfile
|
||||
import functools
|
||||
import threading
|
||||
import types
|
||||
from collections import MutableMapping
|
||||
from zipimport import zipimporter
|
||||
@ -1100,7 +1101,8 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
self.disabled = set(self.opts.get('disable_{0}{1}'.format(
|
||||
self.tag, '' if self.tag[-1] == 's' else 's'), []))
|
||||
|
||||
self.refresh_file_mapping()
|
||||
self._lock = threading.RLock()
|
||||
self._refresh_file_mapping()
|
||||
|
||||
super(LazyLoader, self).__init__() # late init the lazy loader
|
||||
# create all of the import namespaces
|
||||
@ -1167,7 +1169,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
else:
|
||||
return '\'{0}\' __virtual__ returned False'.format(mod_name)
|
||||
|
||||
def refresh_file_mapping(self):
|
||||
def _refresh_file_mapping(self):
|
||||
'''
|
||||
refresh the mapping of the FS on disk
|
||||
'''
|
||||
@ -1285,15 +1287,16 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
'''
|
||||
Clear the dict
|
||||
'''
|
||||
super(LazyLoader, self).clear() # clear the lazy loader
|
||||
self.loaded_files = set()
|
||||
self.missing_modules = {}
|
||||
self.loaded_modules = {}
|
||||
# if we have been loaded before, lets clear the file mapping since
|
||||
# we obviously want a re-do
|
||||
if hasattr(self, 'opts'):
|
||||
self.refresh_file_mapping()
|
||||
self.initial_load = False
|
||||
with self._lock:
|
||||
super(LazyLoader, self).clear() # clear the lazy loader
|
||||
self.loaded_files = set()
|
||||
self.missing_modules = {}
|
||||
self.loaded_modules = {}
|
||||
# if we have been loaded before, lets clear the file mapping since
|
||||
# we obviously want a re-do
|
||||
if hasattr(self, 'opts'):
|
||||
self._refresh_file_mapping()
|
||||
self.initial_load = False
|
||||
|
||||
def __prep_mod_opts(self, opts):
|
||||
'''
|
||||
@ -1504,14 +1507,14 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
virtual_funcs_to_process = ['__virtual__'] + self.virtual_funcs
|
||||
for virtual_func in virtual_funcs_to_process:
|
||||
virtual_ret, module_name, virtual_err, virtual_aliases = \
|
||||
self.process_virtual(mod, module_name)
|
||||
self._process_virtual(mod, module_name)
|
||||
if virtual_err is not None:
|
||||
log.trace(
|
||||
'Error loading %s.%s: %s',
|
||||
self.tag, module_name, virtual_err
|
||||
)
|
||||
|
||||
# if process_virtual returned a non-True value then we are
|
||||
# if _process_virtual returned a non-True value then we are
|
||||
# supposed to not process this module
|
||||
if virtual_ret is not True and module_name not in self.missing_modules:
|
||||
# If a module has information about why it could not be loaded, record it
|
||||
@ -1601,39 +1604,42 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
if not isinstance(key, six.string_types) or '.' not in key:
|
||||
raise KeyError
|
||||
mod_name, _ = key.split('.', 1)
|
||||
if mod_name in self.missing_modules:
|
||||
return True
|
||||
# if the modulename isn't in the whitelist, don't bother
|
||||
if self.whitelist and mod_name not in self.whitelist:
|
||||
raise KeyError
|
||||
with self._lock:
|
||||
# It is possible that the key is in the dictionary after
|
||||
# acquiring the lock due to another thread loading it.
|
||||
if mod_name in self.missing_modules or key in self._dict:
|
||||
return True
|
||||
# if the modulename isn't in the whitelist, don't bother
|
||||
if self.whitelist and mod_name not in self.whitelist:
|
||||
raise KeyError
|
||||
|
||||
def _inner_load(mod_name):
|
||||
for name in self._iter_files(mod_name):
|
||||
if name in self.loaded_files:
|
||||
continue
|
||||
# if we got what we wanted, we are done
|
||||
if self._load_module(name) and key in self._dict:
|
||||
return True
|
||||
return False
|
||||
def _inner_load(mod_name):
|
||||
for name in self._iter_files(mod_name):
|
||||
if name in self.loaded_files:
|
||||
continue
|
||||
# if we got what we wanted, we are done
|
||||
if self._load_module(name) and key in self._dict:
|
||||
return True
|
||||
return False
|
||||
|
||||
# try to load the module
|
||||
ret = None
|
||||
reloaded = False
|
||||
# re-scan up to once, IOErrors or a failed load cause re-scans of the
|
||||
# filesystem
|
||||
while True:
|
||||
try:
|
||||
ret = _inner_load(mod_name)
|
||||
if not reloaded and ret is not True:
|
||||
self.refresh_file_mapping()
|
||||
reloaded = True
|
||||
# try to load the module
|
||||
ret = None
|
||||
reloaded = False
|
||||
# re-scan up to once, IOErrors or a failed load cause re-scans of the
|
||||
# filesystem
|
||||
while True:
|
||||
try:
|
||||
ret = _inner_load(mod_name)
|
||||
if not reloaded and ret is not True:
|
||||
self._refresh_file_mapping()
|
||||
reloaded = True
|
||||
continue
|
||||
break
|
||||
except IOError:
|
||||
if not reloaded:
|
||||
self._refresh_file_mapping()
|
||||
reloaded = True
|
||||
continue
|
||||
break
|
||||
except IOError:
|
||||
if not reloaded:
|
||||
self.refresh_file_mapping()
|
||||
reloaded = True
|
||||
continue
|
||||
|
||||
return ret
|
||||
|
||||
@ -1641,16 +1647,18 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
'''
|
||||
Load all of them
|
||||
'''
|
||||
for name in self.file_mapping:
|
||||
if name in self.loaded_files or name in self.missing_modules:
|
||||
continue
|
||||
self._load_module(name)
|
||||
with self._lock:
|
||||
for name in self.file_mapping:
|
||||
if name in self.loaded_files or name in self.missing_modules:
|
||||
continue
|
||||
self._load_module(name)
|
||||
|
||||
self.loaded = True
|
||||
self.loaded = True
|
||||
|
||||
def reload_modules(self):
|
||||
self.loaded_files = set()
|
||||
self._load_all()
|
||||
with self._lock:
|
||||
self.loaded_files = set()
|
||||
self._load_all()
|
||||
|
||||
def _apply_outputter(self, func, mod):
|
||||
'''
|
||||
@ -1661,7 +1669,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
if func.__name__ in outp:
|
||||
func.__outputter__ = outp[func.__name__]
|
||||
|
||||
def process_virtual(self, mod, module_name, virtual_func='__virtual__'):
|
||||
def _process_virtual(self, mod, module_name, virtual_func='__virtual__'):
|
||||
'''
|
||||
Given a loaded module and its default name determine its virtual name
|
||||
|
||||
|
@ -3038,6 +3038,7 @@ def rm_(name, force=False, volumes=False, **kwargs):
|
||||
'''
|
||||
kwargs = salt.utils.clean_kwargs(**kwargs)
|
||||
stop_ = kwargs.pop('stop', False)
|
||||
auto_remove = False
|
||||
if kwargs:
|
||||
salt.utils.invalid_kwargs(kwargs)
|
||||
|
||||
@ -3047,9 +3048,19 @@ def rm_(name, force=False, volumes=False, **kwargs):
|
||||
'remove this container'.format(name)
|
||||
)
|
||||
if stop_ and not force:
|
||||
inspect_results = inspect_container(name)
|
||||
try:
|
||||
auto_remove = inspect_results['HostConfig']['AutoRemove']
|
||||
except KeyError:
|
||||
log.error(
|
||||
'Failed to find AutoRemove in inspect results, Docker API may '
|
||||
'have changed. Full results: %s', inspect_results
|
||||
)
|
||||
stop(name)
|
||||
pre = ps_(all=True)
|
||||
_client_wrapper('remove_container', name, v=volumes, force=force)
|
||||
|
||||
if not auto_remove:
|
||||
_client_wrapper('remove_container', name, v=volumes, force=force)
|
||||
_clear_context()
|
||||
return [x for x in pre if x not in ps_(all=True)]
|
||||
|
||||
|
@ -25,7 +25,6 @@ import logging
|
||||
|
||||
try:
|
||||
from sense_hat import SenseHat
|
||||
_sensehat = SenseHat()
|
||||
has_sense_hat = True
|
||||
except (ImportError, NameError):
|
||||
_sensehat = None
|
||||
@ -39,14 +38,19 @@ def __virtual__():
|
||||
Only load the module if SenseHat is available
|
||||
'''
|
||||
if has_sense_hat:
|
||||
try:
|
||||
_sensehat = SenseHat()
|
||||
except OSError:
|
||||
return False, 'This module can only be used on a Raspberry Pi with a SenseHat.'
|
||||
|
||||
rotation = __salt__['pillar.get']('sensehat:rotation', 0)
|
||||
if rotation in [0, 90, 180, 270]:
|
||||
_sensehat.set_rotation(rotation, False)
|
||||
else:
|
||||
log.error("{0} is not a valid rotation. Using default rotation.".format(rotation))
|
||||
log.error('{0} is not a valid rotation. Using default rotation.'.format(rotation))
|
||||
return True
|
||||
else:
|
||||
return False, "The SenseHat excecution module can not be loaded: SenseHat unavailable.\nThis module can only be used on a Raspberry Pi with a SenseHat. Also make sure that the sense_hat python library is installed!"
|
||||
|
||||
return False, 'The SenseHat execution module cannot be loaded: \'sense_hat\' python library unavailable.'
|
||||
|
||||
|
||||
def set_pixels(pixels):
|
||||
|
@ -2030,8 +2030,9 @@ def event(tagmatch='*',
|
||||
indent=None if not pretty else 4)))
|
||||
sys.stdout.flush()
|
||||
|
||||
count -= 1
|
||||
log.debug('Remaining event matches: %s', count)
|
||||
if count > 0:
|
||||
count -= 1
|
||||
log.debug('Remaining event matches: %s', count)
|
||||
|
||||
if count == 0:
|
||||
break
|
||||
|
@ -3439,7 +3439,7 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element,
|
||||
element_valuenames = []
|
||||
element_values = this_element_value
|
||||
if this_element_value is not None:
|
||||
element_valuenames = list(range(1, len(this_element_value) + 1))
|
||||
element_valuenames = list([str(z) for z in range(1, len(this_element_value) + 1)])
|
||||
if 'additive' in element.attrib:
|
||||
if element.attrib['additive'].lower() == 'false':
|
||||
# a delete values will be added before all the other
|
||||
@ -3464,11 +3464,18 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element,
|
||||
if this_element_value is not None:
|
||||
element_valuenames = this_element_value.keys()
|
||||
element_values = this_element_value.values()
|
||||
|
||||
if 'valuePrefix' in element.attrib and element.attrib['valuePrefix'] != '':
|
||||
if this_element_value is not None:
|
||||
element_valuenames = ['{0}{1}'.format(element.attrib['valuePrefix'],
|
||||
k) for k in element_valuenames]
|
||||
if 'valuePrefix' in element.attrib:
|
||||
# if the valuePrefix attribute exists, the valuenames are <prefix><number>
|
||||
# most prefixes attributes are empty in the admx files, so the valuenames
|
||||
# end up being just numbers
|
||||
if element.attrib['valuePrefix'] != '':
|
||||
if this_element_value is not None:
|
||||
element_valuenames = ['{0}{1}'.format(element.attrib['valuePrefix'],
|
||||
k) for k in element_valuenames]
|
||||
else:
|
||||
# if there is no valuePrefix attribute, the valuename is the value
|
||||
if element_values is not None:
|
||||
element_valuenames = [str(z) for z in element_values]
|
||||
if not check_deleted:
|
||||
if this_element_value is not None:
|
||||
log.debug('_processValueItem has an explicit element_value of {0}'.format(this_element_value))
|
||||
|
@ -2582,7 +2582,8 @@ def mod_repo(repo, basedir=None, **kwargs):
|
||||
|
||||
# Build a list of keys to be deleted
|
||||
todelete = []
|
||||
for key in repo_opts:
|
||||
# list() of keys because the dict could be shrinking in the for loop.
|
||||
for key in list(repo_opts):
|
||||
if repo_opts[key] != 0 and not repo_opts[key]:
|
||||
del repo_opts[key]
|
||||
todelete.append(key)
|
||||
|
@ -43,7 +43,12 @@ def _handle_interrupt(exc, original_exc, hardfail=False, trace=''):
|
||||
|
||||
|
||||
def _handle_signals(client, signum, sigframe):
|
||||
trace = traceback.format_exc()
|
||||
try:
|
||||
# This raises AttributeError on Python 3.4 and 3.5 if there is no current exception.
|
||||
# Ref: https://bugs.python.org/issue23003
|
||||
trace = traceback.format_exc()
|
||||
except AttributeError:
|
||||
trace = ''
|
||||
try:
|
||||
hardcrash = client.options.hard_crash
|
||||
except (AttributeError, KeyError):
|
||||
|
@ -2434,6 +2434,7 @@ class State(object):
|
||||
'__run_num__': self.__run_num,
|
||||
'__sls__': low['__sls__']
|
||||
}
|
||||
self.pre[tag] = running[tag]
|
||||
self.__run_num += 1
|
||||
elif status == 'change' and not low.get('__prereq__'):
|
||||
ret = self.call(low, chunks, running)
|
||||
@ -2817,6 +2818,7 @@ class BaseHighState(object):
|
||||
'top_file_merging_strategy set to \'same\', but no '
|
||||
'default_top configuration option was set'
|
||||
)
|
||||
self.opts['environment'] = self.opts['default_top']
|
||||
|
||||
if self.opts['environment']:
|
||||
contents = self.client.cache_file(
|
||||
|
@ -20,6 +20,15 @@ def __virtual__():
|
||||
return True
|
||||
|
||||
|
||||
def _norm_key(key):
|
||||
'''
|
||||
Normalize windows environment keys
|
||||
'''
|
||||
if utils.is_windows():
|
||||
return key.upper()
|
||||
return key
|
||||
|
||||
|
||||
def setenv(name,
|
||||
value,
|
||||
false_unsets=False,
|
||||
@ -124,12 +133,11 @@ def setenv(name,
|
||||
permanent_hive = 'HKLM'
|
||||
permanent_key = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
|
||||
|
||||
out = __salt__['reg.read_value'](permanent_hive, permanent_key, key)
|
||||
out = __salt__['reg.read_value'](permanent_hive, permanent_key, _norm_key(key))
|
||||
return out['success'] is True
|
||||
else:
|
||||
return False
|
||||
|
||||
if current_environ.get(key, None) is None and not key_exists():
|
||||
if current_environ.get(_norm_key(key), None) is None and not key_exists():
|
||||
# The key does not exist in environment
|
||||
if false_unsets is not True:
|
||||
# This key will be added with value ''
|
||||
@ -138,13 +146,13 @@ def setenv(name,
|
||||
# The key exists.
|
||||
if false_unsets is not True:
|
||||
# Check to see if the value will change
|
||||
if current_environ.get(key, None) != '':
|
||||
if current_environ.get(_norm_key(key), None) != '':
|
||||
# This key value will change to ''
|
||||
ret['changes'].update({key: ''})
|
||||
else:
|
||||
# We're going to delete the key
|
||||
ret['changes'].update({key: None})
|
||||
elif current_environ.get(key, '') == val:
|
||||
elif current_environ.get(_norm_key(key), '') == val:
|
||||
already_set.append(key)
|
||||
else:
|
||||
ret['changes'].update({key: val})
|
||||
|
@ -607,28 +607,34 @@ def _check_file(name):
|
||||
return ret, msg
|
||||
|
||||
|
||||
def _clean_dir(root, keep, exclude_pat):
|
||||
def _find_keep_files(root, keep):
|
||||
'''
|
||||
Clean out all of the files and directories in a directory (root) while
|
||||
preserving the files in a list (keep) and part of exclude_pat
|
||||
Compile a list of valid keep files (and directories).
|
||||
'''
|
||||
removed = set()
|
||||
real_keep = set()
|
||||
real_keep.add(root)
|
||||
if isinstance(keep, list):
|
||||
for fn_ in keep:
|
||||
if not os.path.isabs(fn_):
|
||||
continue
|
||||
fn_ = os.path.normcase(os.path.abspath(fn_))
|
||||
real_keep.add(fn_)
|
||||
while True:
|
||||
fn_ = os.path.dirname(fn_)
|
||||
fn_ = os.path.abspath(os.path.dirname(fn_))
|
||||
real_keep.add(fn_)
|
||||
if fn_ in [
|
||||
os.sep,
|
||||
''.join([os.path.splitdrive(fn_)[0], os.sep]),
|
||||
''.join([os.path.splitdrive(fn_)[0], os.sep, os.sep])
|
||||
]:
|
||||
drive, path = os.path.splitdrive(fn_)
|
||||
if not path.lstrip(os.sep):
|
||||
break
|
||||
return real_keep
|
||||
|
||||
|
||||
def _clean_dir(root, keep, exclude_pat):
|
||||
'''
|
||||
Clean out all of the files and directories in a directory (root) while
|
||||
preserving the files in a list (keep) and part of exclude_pat
|
||||
'''
|
||||
real_keep = _find_keep_files(root, keep)
|
||||
removed = set()
|
||||
|
||||
def _delete_not_kept(nfn):
|
||||
if nfn not in real_keep:
|
||||
|
@ -265,8 +265,13 @@ def managed(name,
|
||||
ret['comment'] = ' '.join(errors)
|
||||
return ret
|
||||
|
||||
try:
|
||||
currently_enabled = __salt__['ip.is_enabled'](name)
|
||||
except CommandExecutionError:
|
||||
currently_enabled = False
|
||||
|
||||
if not enabled:
|
||||
if __salt__['ip.is_enabled'](name):
|
||||
if currently_enabled:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = ('Interface \'{0}\' will be disabled'
|
||||
@ -280,18 +285,13 @@ def managed(name,
|
||||
ret['comment'] += ' (already disabled)'
|
||||
return ret
|
||||
else:
|
||||
try:
|
||||
currently_enabled = __salt__['ip.is_disabled'](name)
|
||||
except CommandExecutionError:
|
||||
currently_enabled = False
|
||||
if not currently_enabled:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = ('Interface \'{0}\' will be enabled'
|
||||
.format(name))
|
||||
else:
|
||||
result = __salt__['ip.enable'](name)
|
||||
if not result:
|
||||
if not __salt__['ip.enable'](name):
|
||||
ret['result'] = False
|
||||
ret['comment'] = ('Failed to enable interface \'{0}\' to '
|
||||
'make changes'.format(name))
|
||||
|
@ -515,7 +515,10 @@ def bootstrap(vm_, opts=None):
|
||||
'winrm_port', vm_, opts, default=5986
|
||||
)
|
||||
deploy_kwargs['winrm_use_ssl'] = salt.config.get_cloud_config_value(
|
||||
'winrm_use_ssl', vm_, opts, default=True
|
||||
'winrm_use_ssl', vm_, opts, default=True
|
||||
)
|
||||
deploy_kwargs['winrm_verify_ssl'] = salt.config.get_cloud_config_value(
|
||||
'winrm_verify_ssl', vm_, opts, default=True
|
||||
)
|
||||
if saltify_driver:
|
||||
deploy_kwargs['port_timeout'] = 1 # No need to wait/retry with Saltify
|
||||
@ -843,7 +846,7 @@ def wait_for_winexesvc(host, port, username, password, timeout=900):
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def wait_for_winrm(host, port, username, password, timeout=900, use_ssl=True):
|
||||
def wait_for_winrm(host, port, username, password, timeout=900, use_ssl=True, verify=True):
|
||||
'''
|
||||
Wait until WinRM connection can be established.
|
||||
'''
|
||||
@ -853,14 +856,20 @@ def wait_for_winrm(host, port, username, password, timeout=900, use_ssl=True):
|
||||
host, port
|
||||
)
|
||||
)
|
||||
transport = 'ssl'
|
||||
if not use_ssl:
|
||||
transport = 'plaintext'
|
||||
trycount = 0
|
||||
while True:
|
||||
trycount += 1
|
||||
try:
|
||||
transport = 'ssl'
|
||||
if not use_ssl:
|
||||
transport = 'plaintext'
|
||||
s = winrm.Session(host, auth=(username, password), transport=transport)
|
||||
winrm_kwargs = {'target': host,
|
||||
'auth': (username, password),
|
||||
'transport': transport}
|
||||
if not verify:
|
||||
log.debug("SSL validation for WinRM disabled.")
|
||||
winrm_kwargs['server_cert_validation'] = 'ignore'
|
||||
s = winrm.Session(**winrm_kwargs)
|
||||
if hasattr(s.protocol, 'set_timeout'):
|
||||
s.protocol.set_timeout(15)
|
||||
log.trace('WinRM endpoint url: {0}'.format(s.url))
|
||||
@ -1008,6 +1017,7 @@ def deploy_windows(host,
|
||||
use_winrm=False,
|
||||
winrm_port=5986,
|
||||
winrm_use_ssl=True,
|
||||
winrm_verify_ssl=True,
|
||||
**kwargs):
|
||||
'''
|
||||
Copy the install files to a remote Windows box, and execute them
|
||||
@ -1034,7 +1044,8 @@ def deploy_windows(host,
|
||||
if HAS_WINRM and use_winrm:
|
||||
winrm_session = wait_for_winrm(host=host, port=winrm_port,
|
||||
username=username, password=password,
|
||||
timeout=port_timeout * 60, use_ssl=winrm_use_ssl)
|
||||
timeout=port_timeout * 60, use_ssl=winrm_use_ssl,
|
||||
verify=winrm_verify_ssl)
|
||||
if winrm_session is not None:
|
||||
service_available = True
|
||||
else:
|
||||
|
@ -35,6 +35,7 @@ import salt.utils.args
|
||||
import salt.utils.xdg
|
||||
import salt.utils.jid
|
||||
import salt.utils.files
|
||||
import salt.utils.win_functions
|
||||
from salt.utils import kinds
|
||||
from salt.defaults import DEFAULT_TARGET_DELIM
|
||||
from salt.utils.validate.path import is_writeable
|
||||
@ -1017,11 +1018,11 @@ class DaemonMixIn(six.with_metaclass(MixInMeta, object)):
|
||||
if self.check_pidfile():
|
||||
pid = self.get_pidfile()
|
||||
if not salt.utils.is_windows():
|
||||
if self.check_pidfile() and self.is_daemonized(pid) and not os.getppid() == pid:
|
||||
if self.check_pidfile() and self.is_daemonized(pid) and os.getppid() != pid:
|
||||
return True
|
||||
else:
|
||||
# We have no os.getppid() on Windows. Best effort.
|
||||
if self.check_pidfile() and self.is_daemonized(pid):
|
||||
# We have no os.getppid() on Windows. Use salt.utils.win_functions.get_parent_pid
|
||||
if self.check_pidfile() and self.is_daemonized(pid) and salt.utils.win_functions.get_parent_pid() != pid:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -147,7 +147,7 @@ def get_pidfile(pidfile):
|
||||
pid = pdf.read().strip()
|
||||
return int(pid)
|
||||
except (OSError, IOError, TypeError, ValueError):
|
||||
return None
|
||||
return -1
|
||||
|
||||
|
||||
def clean_proc(proc, wait_for_kill=10):
|
||||
|
@ -8,14 +8,18 @@ from __future__ import absolute_import
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import yaml
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.config import cloud_providers_config
|
||||
import salt.utils
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.case import ShellCase
|
||||
from tests.support.paths import FILES
|
||||
from tests.support.helpers import expensiveTest
|
||||
from tests.support.unit import expectedFailure
|
||||
from tests.support import win_installer
|
||||
|
||||
# Import Third-Party Libs
|
||||
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
|
||||
@ -39,6 +43,38 @@ class EC2Test(ShellCase):
|
||||
'''
|
||||
Integration tests for the EC2 cloud provider in Salt-Cloud
|
||||
'''
|
||||
TIMEOUT = 500
|
||||
|
||||
def _installer_name(self):
|
||||
'''
|
||||
Determine the downloaded installer name by searching the files
|
||||
directory for the firt file that loosk like an installer.
|
||||
'''
|
||||
for path, dirs, files in os.walk(FILES):
|
||||
for file in files:
|
||||
if file.startswith(win_installer.PREFIX):
|
||||
return file
|
||||
break
|
||||
return
|
||||
|
||||
def _fetch_latest_installer(self):
|
||||
'''
|
||||
Download the latest Windows installer executable
|
||||
'''
|
||||
name = win_installer.latest_installer_name()
|
||||
path = os.path.join(FILES, name)
|
||||
with salt.utils.fopen(path, 'wb') as fp:
|
||||
win_installer.download_and_verify(fp, name)
|
||||
return name
|
||||
|
||||
def _ensure_installer(self):
|
||||
'''
|
||||
Make sure the testing environment has a Windows installer executbale.
|
||||
'''
|
||||
name = self._installer_name()
|
||||
if name:
|
||||
return name
|
||||
return self._fetch_latest_installer()
|
||||
|
||||
@expensiveTest
|
||||
def setUp(self):
|
||||
@ -90,24 +126,51 @@ class EC2Test(ShellCase):
|
||||
'missing. Check tests/integration/files/conf/cloud.providers.d/{0}.conf'
|
||||
.format(PROVIDER_NAME)
|
||||
)
|
||||
self.INSTALLER = self._ensure_installer()
|
||||
|
||||
def test_instance(self):
|
||||
def override_profile_config(self, name, data):
|
||||
conf_path = os.path.join(self.get_config_dir(), 'cloud.profiles.d', 'ec2.conf')
|
||||
with salt.utils.fopen(conf_path, 'r') as fp:
|
||||
conf = yaml.safe_load(fp)
|
||||
conf[name].update(data)
|
||||
with salt.utils.fopen(conf_path, 'w') as fp:
|
||||
yaml.dump(conf, fp)
|
||||
|
||||
def copy_file(self, name):
|
||||
'''
|
||||
Copy a file from tests/integration/files to a test's temporary
|
||||
configuration directory. The path to the file which is created will be
|
||||
returned.
|
||||
'''
|
||||
src = os.path.join(FILES, name)
|
||||
dst = os.path.join(self.get_config_dir(), name)
|
||||
with salt.utils.fopen(src, 'rb') as sfp:
|
||||
with salt.utils.fopen(dst, 'wb') as dfp:
|
||||
dfp.write(sfp.read())
|
||||
return dst
|
||||
|
||||
def _test_instance(self, profile='ec2-test', debug=False, timeout=TIMEOUT):
|
||||
'''
|
||||
Tests creating and deleting an instance on EC2 (classic)
|
||||
'''
|
||||
|
||||
# create the instance
|
||||
instance = self.run_cloud('-p ec2-test {0}'.format(INSTANCE_NAME), timeout=500)
|
||||
cmd = '-p {0}'.format(profile)
|
||||
if debug:
|
||||
cmd += ' -l debug'
|
||||
cmd += ' {0}'.format(INSTANCE_NAME)
|
||||
instance = self.run_cloud(cmd, timeout=timeout)
|
||||
ret_str = '{0}:'.format(INSTANCE_NAME)
|
||||
|
||||
# check if instance returned with salt installed
|
||||
try:
|
||||
self.assertIn(ret_str, instance)
|
||||
except AssertionError:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=timeout)
|
||||
raise
|
||||
|
||||
# delete the instance
|
||||
delete = self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
delete = self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=timeout)
|
||||
ret_str = ' shutting-down'
|
||||
|
||||
# check if deletion was performed appropriately
|
||||
@ -151,6 +214,80 @@ class EC2Test(ShellCase):
|
||||
# check if deletion was performed appropriately
|
||||
self.assertIn(ret_str, delete)
|
||||
|
||||
def test_instance(self):
|
||||
'''
|
||||
Tests creating and deleting an instance on EC2 (classic)
|
||||
'''
|
||||
self._test_instance('ec2-test')
|
||||
|
||||
@expectedFailure
|
||||
def test_win2012r2_winexe(self):
|
||||
'''
|
||||
Tests creating and deleting a Windows 2012r2instance on EC2 using
|
||||
winexe (classic)
|
||||
'''
|
||||
# TODO: winexe calls hang and the test fails by timing out. The same
|
||||
# same calls succeed when run outside of the test environment.
|
||||
self.override_profile_config(
|
||||
'ec2-win2012-test',
|
||||
{
|
||||
'use_winrm': False,
|
||||
'user_data': self.copy_file('windows-firewall-winexe.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
},
|
||||
)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=500)
|
||||
|
||||
def test_win2012r2_winrm(self):
|
||||
'''
|
||||
Tests creating and deleting a Windows 2012r2 instance on EC2 using
|
||||
winrm (classic)
|
||||
'''
|
||||
self.override_profile_config(
|
||||
'ec2-win2016-test',
|
||||
{
|
||||
'user_data': self.copy_file('windows-firewall.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
'winrm_ssl_verify': False,
|
||||
}
|
||||
|
||||
)
|
||||
self._test_instance('ec2-win2012r2-test', debug=True, timeout=500)
|
||||
|
||||
@expectedFailure
|
||||
def test_win2016_winexe(self):
|
||||
'''
|
||||
Tests creating and deleting a Windows 2016 instance on EC2 using winrm
|
||||
(classic)
|
||||
'''
|
||||
# TODO: winexe calls hang and the test fails by timing out. The same
|
||||
# same calls succeed when run outside of the test environment.
|
||||
self.override_profile_config(
|
||||
'ec2-win2016-test',
|
||||
{
|
||||
'use_winrm': False,
|
||||
'user_data': self.copy_file('windows-firewall-winexe.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
},
|
||||
)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=500)
|
||||
|
||||
def test_win2016_winrm(self):
|
||||
'''
|
||||
Tests creating and deleting a Windows 2016 instance on EC2 using winrm
|
||||
(classic)
|
||||
'''
|
||||
self.override_profile_config(
|
||||
'ec2-win2016-test',
|
||||
{
|
||||
'user_data': self.copy_file('windows-firewall.ps1'),
|
||||
'win_installer': self.copy_file(self.INSTALLER),
|
||||
'winrm_ssl_verify': False,
|
||||
}
|
||||
|
||||
)
|
||||
self._test_instance('ec2-win2016-test', debug=True, timeout=500)
|
||||
|
||||
def tearDown(self):
|
||||
'''
|
||||
Clean up after tests
|
||||
@ -160,4 +297,4 @@ class EC2Test(ShellCase):
|
||||
|
||||
# if test instance is still present, delete it
|
||||
if ret_str in query:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=self.TIMEOUT)
|
||||
|
1
tests/integration/doc/__init__.py
Normal file
1
tests/integration/doc/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
104
tests/integration/doc/test_man.py
Normal file
104
tests/integration/doc/test_man.py
Normal file
@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Tests for existence of manpages
|
||||
'''
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
import shutil
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.paths import TMP
|
||||
from tests.support.unit import skipIf
|
||||
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'minion is windows')
|
||||
class ManTest(ModuleCase):
|
||||
rootdir = os.path.join(TMP, 'mantest')
|
||||
# Map filenames to search strings which should be in the manpage
|
||||
manpages = {
|
||||
'salt-cp.1': [
|
||||
'salt-cp Documentation',
|
||||
'copies files from the master',
|
||||
],
|
||||
'salt-cloud.1': [
|
||||
'Salt Cloud Command',
|
||||
'Provision virtual machines in the cloud',
|
||||
],
|
||||
'salt-call.1': [
|
||||
'salt-call Documentation',
|
||||
'run module functions locally',
|
||||
],
|
||||
'salt-api.1': [
|
||||
'salt-api Command',
|
||||
'Start interfaces used to remotely connect',
|
||||
],
|
||||
'salt-unity.1': [
|
||||
'salt-unity Command',
|
||||
'unified invocation wrapper',
|
||||
],
|
||||
'salt-syndic.1': [
|
||||
'salt-syndic Documentation',
|
||||
'Salt syndic daemon',
|
||||
],
|
||||
'salt-ssh.1': [
|
||||
'salt-ssh Documentation',
|
||||
'executed using only SSH',
|
||||
],
|
||||
'salt-run.1': [
|
||||
'salt-run Documentation',
|
||||
'frontend command for executing',
|
||||
],
|
||||
'salt-proxy.1': [
|
||||
'salt-proxy Documentation',
|
||||
'proxies these commands',
|
||||
],
|
||||
'salt-minion.1': [
|
||||
'salt-minion Documentation',
|
||||
'Salt minion daemon',
|
||||
],
|
||||
'salt-master.1': [
|
||||
'salt-master Documentation',
|
||||
'Salt master daemon',
|
||||
],
|
||||
'salt-key.1': [
|
||||
'salt-key Documentation',
|
||||
'management of Salt server public keys',
|
||||
],
|
||||
'salt.1': [
|
||||
'allows for commands to be executed',
|
||||
],
|
||||
'salt.7': [
|
||||
'Salt Documentation',
|
||||
],
|
||||
'spm.1': [
|
||||
'Salt Package Manager Command',
|
||||
'command for managing Salt packages',
|
||||
],
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
if not self.run_function('mantest.install', [self.rootdir]):
|
||||
self.fail('Failed to install salt to {0}'.format(self.rootdir))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
shutil.rmtree(cls.rootdir)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_man(self):
|
||||
'''
|
||||
Make sure that man pages are installed
|
||||
'''
|
||||
ret = self.run_function('mantest.search', [self.manpages, self.rootdir])
|
||||
# The above function returns True if successful and an exception (which
|
||||
# will manifest in the return as a stringified exception) if
|
||||
# unsuccessful. Therefore, a simple assertTrue is not sufficient.
|
||||
if ret is not True:
|
||||
self.fail(ret)
|
@ -4,3 +4,31 @@ ec2-test:
|
||||
size: t1.micro
|
||||
sh_username: ec2-user
|
||||
script_args: '-P -Z'
|
||||
ec2-win2012r2-test:
|
||||
provider: ec2-config
|
||||
size: t2.micro
|
||||
image: ami-eb1ecd96
|
||||
smb_port: 445
|
||||
win_installer: ''
|
||||
win_username: Administrator
|
||||
win_password: auto
|
||||
userdata_file: ''
|
||||
userdata_template: False
|
||||
use_winrm: True
|
||||
winrm_verify_ssl: False
|
||||
ssh_interface: private_ips
|
||||
deploy: True
|
||||
ec2-win2016-test:
|
||||
provider: ec2-config
|
||||
size: t2.micro
|
||||
image: ami-ed14c790
|
||||
smb_port: 445
|
||||
win_installer: ''
|
||||
win_username: Administrator
|
||||
win_password: auto
|
||||
userdata_file: ''
|
||||
userdata_template: False
|
||||
use_winrm: True
|
||||
winrm_verify_ssl: False
|
||||
ssh_interface: private_ips
|
||||
deploy: True
|
||||
|
74
tests/integration/files/file/base/_modules/mantest.py
Normal file
74
tests/integration/files/file/base/_modules/mantest.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Helpers for testing man pages
|
||||
'''
|
||||
# Import python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
# Import Salt Tesing libs
|
||||
from tests.support.paths import CODE_DIR
|
||||
|
||||
|
||||
def install(rootdir):
|
||||
if not os.path.exists(rootdir):
|
||||
os.makedirs(rootdir)
|
||||
return __salt__['cmd.retcode'](
|
||||
[sys.executable,
|
||||
os.path.join(CODE_DIR, 'setup.py'),
|
||||
'install', '--root={0}'.format(rootdir)]) == 0
|
||||
return True
|
||||
|
||||
|
||||
def search(manpages, rootdir):
|
||||
manpage_fns = set(manpages)
|
||||
manpage_paths = {}
|
||||
for root, _, files in os.walk(rootdir):
|
||||
if not manpage_fns:
|
||||
# All manpages found, no need to keep walking
|
||||
break
|
||||
# Using list because we will be modifying the set during iteration
|
||||
for manpage_fn in list(manpage_fns):
|
||||
if manpage_fn in files:
|
||||
manpage_path = salt.utils.path_join(root, manpage_fn)
|
||||
manpage_paths[manpage_fn] = manpage_path
|
||||
manpage_fns.remove(manpage_fn)
|
||||
|
||||
if manpage_fns:
|
||||
raise CommandExecutionError(
|
||||
'The following manpages were not found under {0}: {1}'.format(
|
||||
rootdir,
|
||||
', '.join(sorted(manpage_fns))
|
||||
)
|
||||
)
|
||||
|
||||
failed = {}
|
||||
for manpage in sorted(manpages):
|
||||
with salt.utils.fopen(manpage_paths[manpage]) as fp_:
|
||||
contents = salt.utils.to_unicode(fp_.read())
|
||||
# Check for search string in contents
|
||||
for search_string in manpages[manpage]:
|
||||
if search_string not in contents:
|
||||
failed.setdefault(manpage, []).append(
|
||||
'No match for search string \'{0}\' found in {1}'.format(
|
||||
search_string, manpage_paths[manpage]
|
||||
)
|
||||
)
|
||||
# Check for correct install dir
|
||||
path = '/man{0}/'.format(manpage.rsplit('.', 1)[-1])
|
||||
if path not in manpage_paths[manpage]:
|
||||
failed.setdefault(manpage, []).append(
|
||||
'{0} not found in manpage path {1}'.format(
|
||||
path, manpage_paths[manpage]
|
||||
)
|
||||
)
|
||||
|
||||
if failed:
|
||||
raise CommandExecutionError('One or more manpages failed', info=failed)
|
||||
|
||||
return True
|
@ -63,6 +63,8 @@ def get_invalid_docs():
|
||||
'log.warning',
|
||||
'lowpkg.bin_pkg_info',
|
||||
'lxc.run_cmd',
|
||||
'mantest.install',
|
||||
'mantest.search',
|
||||
'nspawn.restart',
|
||||
'nspawn.stop',
|
||||
'pkg.expand_repo_def',
|
||||
|
5
tests/integration/files/windows-firewall-winexe.ps1
Normal file
5
tests/integration/files/windows-firewall-winexe.ps1
Normal file
@ -0,0 +1,5 @@
|
||||
<powershell>
|
||||
New-NetFirewallRule -Name "SMB445" -DisplayName "SMB445" -Protocol TCP -LocalPort 445
|
||||
Set-Item (dir wsman:\localhost\Listener\*\Port -Recurse).pspath 445 -Force
|
||||
Restart-Service winrm
|
||||
</powershell>
|
33
tests/integration/files/windows-firewall.ps1
Normal file
33
tests/integration/files/windows-firewall.ps1
Normal file
@ -0,0 +1,33 @@
|
||||
<powershell>
|
||||
New-NetFirewallRule -Name "SMB445" -DisplayName "SMB445" -Protocol TCP -LocalPort 445
|
||||
New-NetFirewallRule -Name "WINRM5986" -DisplayName "WINRM5986" -Protocol TCP -LocalPort 5986
|
||||
|
||||
winrm quickconfig -q
|
||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="300"}'
|
||||
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
|
||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
||||
|
||||
$SourceStoreScope = 'LocalMachine'
|
||||
$SourceStorename = 'Remote Desktop'
|
||||
|
||||
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScope
|
||||
$SourceStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
|
||||
|
||||
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
|
||||
$_.subject -like '*'
|
||||
}
|
||||
|
||||
$DestStoreScope = 'LocalMachine'
|
||||
$DestStoreName = 'My'
|
||||
|
||||
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
|
||||
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
|
||||
$DestStore.Add($cert)
|
||||
|
||||
$SourceStore.Close()
|
||||
$DestStore.Close()
|
||||
|
||||
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{Hostname=`"($certId)`"`;CertificateThumbprint=`"($cert.Thumbprint)`"`}
|
||||
|
||||
Restart-Service winrm
|
||||
</powershell>
|
@ -4,7 +4,6 @@
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import uuid
|
||||
import shutil
|
||||
import hashlib
|
||||
import logging
|
||||
import psutil
|
||||
@ -15,7 +14,10 @@ import textwrap
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.helpers import get_unused_localhost_port, skip_if_not_root
|
||||
from tests.support.helpers import (
|
||||
get_unused_localhost_port,
|
||||
skip_if_not_root,
|
||||
with_tempfile)
|
||||
from tests.support.unit import skipIf
|
||||
import tests.support.paths as paths
|
||||
|
||||
@ -30,11 +32,11 @@ class CPModuleTest(ModuleCase):
|
||||
'''
|
||||
Validate the cp module
|
||||
'''
|
||||
def test_get_file(self):
|
||||
@with_tempfile
|
||||
def test_get_file(self, tgt):
|
||||
'''
|
||||
cp.get_file
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'scene33')
|
||||
self.run_function(
|
||||
'cp.get_file',
|
||||
[
|
||||
@ -46,11 +48,11 @@ class CPModuleTest(ModuleCase):
|
||||
self.assertIn('KNIGHT: They\'re nervous, sire.', data)
|
||||
self.assertNotIn('bacon', data)
|
||||
|
||||
def test_get_file_templated_paths(self):
|
||||
@with_tempfile
|
||||
def test_get_file_templated_paths(self, tgt):
|
||||
'''
|
||||
cp.get_file
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'cheese')
|
||||
self.run_function(
|
||||
'cp.get_file',
|
||||
[
|
||||
@ -64,11 +66,11 @@ class CPModuleTest(ModuleCase):
|
||||
self.assertIn('Gromit', data)
|
||||
self.assertNotIn('bacon', data)
|
||||
|
||||
def test_get_file_gzipped(self):
|
||||
@with_tempfile
|
||||
def test_get_file_gzipped(self, tgt):
|
||||
'''
|
||||
cp.get_file
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'file.big')
|
||||
src = os.path.join(paths.FILES, 'file', 'base', 'file.big')
|
||||
with salt.utils.fopen(src, 'r') as fp_:
|
||||
data = fp_.read()
|
||||
@ -111,11 +113,11 @@ class CPModuleTest(ModuleCase):
|
||||
self.assertIn('KNIGHT: They\'re nervous, sire.', data)
|
||||
self.assertNotIn('bacon', data)
|
||||
|
||||
def test_get_template(self):
|
||||
@with_tempfile
|
||||
def test_get_template(self, tgt):
|
||||
'''
|
||||
cp.get_template
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'scene33')
|
||||
self.run_function(
|
||||
'cp.get_template',
|
||||
['salt://grail/scene33', tgt],
|
||||
@ -160,11 +162,11 @@ class CPModuleTest(ModuleCase):
|
||||
|
||||
# cp.get_url tests
|
||||
|
||||
def test_get_url(self):
|
||||
@with_tempfile
|
||||
def test_get_url(self, tgt):
|
||||
'''
|
||||
cp.get_url with salt:// source given
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'scene33')
|
||||
self.run_function(
|
||||
'cp.get_url',
|
||||
[
|
||||
@ -235,11 +237,11 @@ class CPModuleTest(ModuleCase):
|
||||
])
|
||||
self.assertEqual(ret, False)
|
||||
|
||||
def test_get_url_https(self):
|
||||
@with_tempfile
|
||||
def test_get_url_https(self, tgt):
|
||||
'''
|
||||
cp.get_url with https:// source given
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'test_get_url_https')
|
||||
self.run_function(
|
||||
'cp.get_url',
|
||||
[
|
||||
@ -576,30 +578,24 @@ class CPModuleTest(ModuleCase):
|
||||
self.assertEqual(
|
||||
sha256_hash['hsum'], hashlib.sha256(data).hexdigest())
|
||||
|
||||
def test_get_file_from_env_predefined(self):
|
||||
@with_tempfile
|
||||
def test_get_file_from_env_predefined(self, tgt):
|
||||
'''
|
||||
cp.get_file
|
||||
'''
|
||||
tgt = os.path.join(paths.TMP, 'cheese')
|
||||
try:
|
||||
self.run_function('cp.get_file', ['salt://cheese', tgt])
|
||||
with salt.utils.fopen(tgt, 'r') as cheese:
|
||||
data = cheese.read()
|
||||
self.assertIn('Gromit', data)
|
||||
self.assertNotIn('Comte', data)
|
||||
finally:
|
||||
os.unlink(tgt)
|
||||
self.run_function('cp.get_file', ['salt://cheese', tgt])
|
||||
with salt.utils.fopen(tgt, 'r') as cheese:
|
||||
data = cheese.read()
|
||||
self.assertIn('Gromit', data)
|
||||
self.assertNotIn('Comte', data)
|
||||
|
||||
def test_get_file_from_env_in_url(self):
|
||||
tgt = os.path.join(paths.TMP, 'cheese')
|
||||
try:
|
||||
self.run_function('cp.get_file', ['salt://cheese?saltenv=prod', tgt])
|
||||
with salt.utils.fopen(tgt, 'r') as cheese:
|
||||
data = cheese.read()
|
||||
self.assertIn('Gromit', data)
|
||||
self.assertIn('Comte', data)
|
||||
finally:
|
||||
os.unlink(tgt)
|
||||
@with_tempfile
|
||||
def test_get_file_from_env_in_url(self, tgt):
|
||||
self.run_function('cp.get_file', ['salt://cheese?saltenv=prod', tgt])
|
||||
with salt.utils.fopen(tgt, 'r') as cheese:
|
||||
data = cheese.read()
|
||||
self.assertIn('Gromit', data)
|
||||
self.assertIn('Comte', data)
|
||||
|
||||
def test_push(self):
|
||||
log_to_xfer = os.path.join(paths.TMP, uuid.uuid4().hex)
|
||||
|
@ -8,9 +8,11 @@ import random
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.helpers import destructiveTest, skip_if_not_root
|
||||
from tests.support.unit import skipIf
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext.six.moves import range
|
||||
import salt.utils
|
||||
|
||||
|
||||
@skip_if_not_root
|
||||
@ -30,13 +32,13 @@ class GroupModuleTest(ModuleCase):
|
||||
self._no_user = self.__random_string()
|
||||
self._group = self.__random_string()
|
||||
self._no_group = self.__random_string()
|
||||
self._gid = 64989
|
||||
self._new_gid = 64998
|
||||
os_grain = self.run_function('grains.item', ['kernel'])
|
||||
if os_grain['kernel'] not in 'Linux':
|
||||
self.os_grain = self.run_function('grains.item', ['kernel'])
|
||||
self._gid = 64989 if 'Windows' not in self.os_grain['kernel'] else None
|
||||
self._new_gid = 64998 if 'Windows' not in self.os_grain['kernel'] else None
|
||||
if self.os_grain['kernel'] not in ('Linux', 'Windows'):
|
||||
self.skipTest(
|
||||
'Test not applicable to \'{kernel}\' kernel'.format(
|
||||
**os_grain
|
||||
**self.os_grain
|
||||
)
|
||||
)
|
||||
|
||||
@ -62,12 +64,18 @@ class GroupModuleTest(ModuleCase):
|
||||
Test the add group function
|
||||
'''
|
||||
# add a new group
|
||||
self.assertTrue(self.run_function('group.add', [self._group, self._gid]))
|
||||
self.assertTrue(self.run_function('group.add', [self._group], gid=self._gid))
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
self.assertEqual(group_info['name'], self._group)
|
||||
self.assertEqual(group_info['gid'], self._gid)
|
||||
self.assertEqual(group_info['name'], self._group)
|
||||
# try adding the group again
|
||||
self.assertFalse(self.run_function('group.add', [self._group, self._gid]))
|
||||
if self.os_grain['kernel'] == 'Windows':
|
||||
add_group = self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.assertEqual(add_group['result'], None)
|
||||
self.assertEqual(add_group['comment'], 'The group {0} already exists.'.format(self._group))
|
||||
self.assertEqual(add_group['changes'], [])
|
||||
else:
|
||||
self.assertFalse(self.run_function('group.add', [self._group], gid=self._gid))
|
||||
|
||||
def test_delete(self):
|
||||
'''
|
||||
@ -79,26 +87,32 @@ class GroupModuleTest(ModuleCase):
|
||||
self.assertTrue(self.run_function('group.delete', [self._group]))
|
||||
|
||||
# group does not exist
|
||||
self.assertFalse(self.run_function('group.delete', [self._no_group]))
|
||||
if self.os_grain['kernel'] == 'Windows':
|
||||
del_group = self.run_function('group.delete', [self._no_group])
|
||||
self.assertEqual(del_group['changes'], [])
|
||||
self.assertEqual(del_group['comment'], 'The group {0} does not exists.'.format(self._no_group))
|
||||
else:
|
||||
self.assertFalse(self.run_function('group.delete', [self._no_group]))
|
||||
|
||||
def test_info(self):
|
||||
'''
|
||||
Test the info group function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.run_function('user.add', [self._user])
|
||||
self.run_function('group.adduser', [self._group, self._user])
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
|
||||
self.assertEqual(group_info['name'], self._group)
|
||||
self.assertEqual(group_info['gid'], self._gid)
|
||||
self.assertIn(self._user, group_info['members'])
|
||||
self.assertIn(self._user, str(group_info['members']))
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'gid test skipped on windows')
|
||||
def test_chgid(self):
|
||||
'''
|
||||
Test the change gid function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.assertTrue(self.run_function('group.chgid', [self._group, self._new_gid]))
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
self.assertEqual(group_info['gid'], self._new_gid)
|
||||
@ -107,47 +121,55 @@ class GroupModuleTest(ModuleCase):
|
||||
'''
|
||||
Test the add user to group function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.run_function('user.add', [self._user])
|
||||
self.assertTrue(self.run_function('group.adduser', [self._group, self._user]))
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
self.assertIn(self._user, group_info['members'])
|
||||
# try add a non existing user
|
||||
self.assertFalse(self.run_function('group.adduser', [self._group, self._no_user]))
|
||||
# try add a user to non existing group
|
||||
self.assertFalse(self.run_function('group.adduser', [self._no_group, self._user]))
|
||||
# try add a non existing user to a non existing group
|
||||
self.assertFalse(self.run_function('group.adduser', [self._no_group, self._no_user]))
|
||||
self.assertIn(self._user, str(group_info['members']))
|
||||
if self.os_grain['kernel'] == 'Windows':
|
||||
no_group = self.run_function('group.adduser', [self._no_group, self._no_user])
|
||||
no_user = self.run_function('group.adduser', [self._group, self._no_user])
|
||||
funcs = [no_group, no_user]
|
||||
for func in funcs:
|
||||
self.assertIn('Fail', func['comment'])
|
||||
self.assertFalse(func['result'])
|
||||
else:
|
||||
# try add a non existing user
|
||||
self.assertFalse(self.run_function('group.adduser', [self._group, self._no_user]))
|
||||
# try add a user to non existing group
|
||||
self.assertFalse(self.run_function('group.adduser', [self._no_group, self._user]))
|
||||
# try add a non existing user to a non existing group
|
||||
self.assertFalse(self.run_function('group.adduser', [self._no_group, self._no_user]))
|
||||
|
||||
def test_deluser(self):
|
||||
'''
|
||||
Test the delete user from group function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.run_function('user.add', [self._user])
|
||||
self.run_function('group.adduser', [self._group, self._user])
|
||||
self.assertTrue(self.run_function('group.deluser', [self._group, self._user]))
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
self.assertNotIn(self._user, group_info['members'])
|
||||
self.assertNotIn(self._user, str(group_info['members']))
|
||||
|
||||
def test_members(self):
|
||||
'''
|
||||
Test the members function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.run_function('user.add', [self._user])
|
||||
self.run_function('user.add', [self._user1])
|
||||
m = '{0},{1}'.format(self._user, self._user1)
|
||||
self.assertTrue(self.run_function('group.members', [self._group, m]))
|
||||
group_info = self.run_function('group.info', [self._group])
|
||||
self.assertIn(self._user, group_info['members'])
|
||||
self.assertIn(self._user1, group_info['members'])
|
||||
self.assertIn(self._user, str(group_info['members']))
|
||||
self.assertIn(self._user1, str(group_info['members']))
|
||||
|
||||
def test_getent(self):
|
||||
'''
|
||||
Test the getent function
|
||||
'''
|
||||
self.run_function('group.add', [self._group, self._gid])
|
||||
self.run_function('group.add', [self._group], gid=self._gid)
|
||||
self.run_function('user.add', [self._user])
|
||||
self.run_function('group.adduser', [self._group, self._user])
|
||||
ginfo = self.run_function('user.getent')
|
||||
|
@ -84,7 +84,8 @@ class SaltUtilSyncModuleTest(ModuleCase):
|
||||
'beacons': [],
|
||||
'utils': [],
|
||||
'returners': [],
|
||||
'modules': ['modules.override_test',
|
||||
'modules': ['modules.mantest',
|
||||
'modules.override_test',
|
||||
'modules.runtests_decorators',
|
||||
'modules.runtests_helpers',
|
||||
'modules.salttest'],
|
||||
@ -127,7 +128,8 @@ class SaltUtilSyncModuleTest(ModuleCase):
|
||||
'beacons': [],
|
||||
'utils': [],
|
||||
'returners': [],
|
||||
'modules': ['modules.override_test',
|
||||
'modules': ['modules.mantest',
|
||||
'modules.override_test',
|
||||
'modules.runtests_helpers',
|
||||
'modules.salttest'],
|
||||
'renderers': [],
|
||||
|
@ -1,44 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Tests man spm
|
||||
'''
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.case import ModuleCase
|
||||
from tests.support.helpers import destructiveTest
|
||||
from tests.support.paths import CODE_DIR
|
||||
|
||||
|
||||
@destructiveTest
|
||||
class SPMManTest(ModuleCase):
|
||||
'''
|
||||
Validate man spm
|
||||
'''
|
||||
|
||||
def setUp(self):
|
||||
self.tmpdir = tempfile.mktemp()
|
||||
os.mkdir(self.tmpdir)
|
||||
self.run_function('cmd.run', ['{0} {1} install --root={2}'.format(
|
||||
sys.executable,
|
||||
os.path.join(CODE_DIR, 'setup.py'),
|
||||
self.tmpdir
|
||||
)])
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
|
||||
def test_man_spm(self):
|
||||
'''
|
||||
test man spm
|
||||
'''
|
||||
manpath = self.run_function('cmd.run', ['find {0} -name spm.1'.format(self.tmpdir)])
|
||||
self.assertIn('/man1/', manpath)
|
||||
cmd = self.run_function('cmd.run', ['man {0}'.format(manpath)])
|
||||
self.assertIn('Salt Package Manager', cmd)
|
||||
self.assertIn('command for managing Salt packages', cmd)
|
@ -33,7 +33,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
Basic test to determine if NPM module was successfully installed and
|
||||
removed.
|
||||
'''
|
||||
ret = self.run_state('npm.installed', name='pm2')
|
||||
ret = self.run_state('npm.installed', name='pm2', registry="http://registry.npmjs.org/")
|
||||
self.assertSaltTrueReturn(ret)
|
||||
ret = self.run_state('npm.removed', name='pm2')
|
||||
self.assertSaltTrueReturn(ret)
|
||||
@ -51,7 +51,11 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
else:
|
||||
user = None
|
||||
npm_dir = None
|
||||
ret = self.run_state('npm.installed', name='request/request#v2.81.1', runas=user, dir=npm_dir)
|
||||
ret = self.run_state('npm.installed',
|
||||
name='request/request#v2.81.1',
|
||||
runas=user,
|
||||
dir=npm_dir,
|
||||
registry="http://registry.npmjs.org/")
|
||||
self.assertSaltTrueReturn(ret)
|
||||
ret = self.run_state('npm.removed', name='git://github.com/request/request', runas=user, dir=npm_dir)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
@ -65,7 +69,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
Basic test to determine if NPM module successfully installs multiple
|
||||
packages.
|
||||
'''
|
||||
ret = self.run_state('npm.installed', name=None, pkgs=['pm2', 'grunt'])
|
||||
ret = self.run_state('npm.installed', name=None, pkgs=['pm2', 'grunt'], registry="http://registry.npmjs.org/")
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
@skipIf(salt.utils.which('npm') and LooseVersion(cmd.run('npm -v')) >= LooseVersion(MAX_NPM_VERSION),
|
||||
|
@ -112,6 +112,9 @@ TEST_SUITES = {
|
||||
'client':
|
||||
{'display_name': 'Client',
|
||||
'path': 'integration/client'},
|
||||
'doc':
|
||||
{'display_name': 'Documentation',
|
||||
'path': 'integration/doc'},
|
||||
'ext_pillar':
|
||||
{'display_name': 'External Pillar',
|
||||
'path': 'integration/pillar'},
|
||||
@ -277,6 +280,15 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
|
||||
action='store_true',
|
||||
help='Run tests for client'
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
'-d',
|
||||
'--doc',
|
||||
'--doc-tests',
|
||||
dest='doc',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Run tests for documentation'
|
||||
)
|
||||
self.test_selection_group.add_option(
|
||||
'-I',
|
||||
'--ext-pillar',
|
||||
|
96
tests/support/win_installer.py
Normal file
96
tests/support/win_installer.py
Normal file
@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details.
|
||||
:license: Apache 2.0, see LICENSE for more details.
|
||||
|
||||
|
||||
tests.support.win_installer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Fetches the binary Windows installer
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
import hashlib
|
||||
import requests
|
||||
import re
|
||||
|
||||
PREFIX = 'Salt-Minion-'
|
||||
REPO = "https://repo.saltstack.com/windows"
|
||||
|
||||
|
||||
def iter_installers(content):
|
||||
'''
|
||||
Parse a list of windows installer links and their corresponding md5
|
||||
checksum links.
|
||||
'''
|
||||
HREF_RE = "<a href=\"(.*?)\">"
|
||||
installer, md5 = None, None
|
||||
for m in re.finditer(HREF_RE, content):
|
||||
x = m.groups()[0]
|
||||
if not x.startswith(PREFIX):
|
||||
continue
|
||||
if x.endswith('zip'):
|
||||
continue
|
||||
if installer:
|
||||
if x != installer + '.md5':
|
||||
raise Exception("Unable to parse response")
|
||||
md5 = x
|
||||
yield installer, md5
|
||||
installer, md5 = None, None
|
||||
else:
|
||||
installer = x
|
||||
|
||||
|
||||
def split_installer(name):
|
||||
'''
|
||||
Return a tuple of the salt version, python verison and architecture from an
|
||||
installer name.
|
||||
'''
|
||||
x = name[len(PREFIX):]
|
||||
return x.split('-')[:3]
|
||||
|
||||
|
||||
def latest_version(repo=REPO):
|
||||
'''
|
||||
Return the latest version found on the salt repository webpage.
|
||||
'''
|
||||
for name, md5 in iter_installers(requests.get(repo).content):
|
||||
pass
|
||||
return split_installer(name)[0]
|
||||
|
||||
|
||||
def installer_name(salt_ver, py_ver='Py2', arch='AMD64'):
|
||||
'''
|
||||
Create an installer file name
|
||||
'''
|
||||
return "Salt-Minion-{}-{}-{}-Setup.exe".format(salt_ver, py_ver, arch)
|
||||
|
||||
|
||||
def latest_installer_name(repo=REPO, **kwargs):
|
||||
'''
|
||||
Fetch the latest installer name
|
||||
'''
|
||||
return installer_name(latest_version(repo), **kwargs)
|
||||
|
||||
|
||||
def download_and_verify(fp, name, repo=REPO):
|
||||
'''
|
||||
Download an installer and verify it's contents.
|
||||
'''
|
||||
md5 = "{}.md5".format(name)
|
||||
url = lambda x: "{}/{}".format(repo, x)
|
||||
resp = requests.get(url(md5))
|
||||
if resp.status_code != 200:
|
||||
raise Exception("Unable to fetch installer md5")
|
||||
installer_md5 = resp.text.strip().split()[0].lower()
|
||||
resp = requests.get(url(name), stream=True)
|
||||
if resp.status_code != 200:
|
||||
raise Exception("Unable to fetch installer")
|
||||
md5hsh = hashlib.md5()
|
||||
for chunk in resp.iter_content(chunk_size=1024):
|
||||
md5hsh.update(chunk)
|
||||
fp.write(chunk)
|
||||
if md5hsh.hexdigest() != installer_md5:
|
||||
raise Exception("Installer's hash does not match {} != {}".format(
|
||||
md5hsh.hexdigest(), installer_md5
|
||||
))
|
@ -19,6 +19,8 @@
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import errno
|
||||
import subprocess
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
@ -33,7 +35,33 @@ from tests.support.mock import (
|
||||
from salt.modules.inspectlib.collector import Inspector
|
||||
|
||||
|
||||
HAS_SYMLINKS = None
|
||||
|
||||
|
||||
def no_symlinks():
|
||||
'''
|
||||
Check if git is installed and has symlinks enabled in the configuration.
|
||||
'''
|
||||
global HAS_SYMLINKS
|
||||
if HAS_SYMLINKS is not None:
|
||||
return not HAS_SYMLINKS
|
||||
output = ''
|
||||
try:
|
||||
output = subprocess.check_output('git config --get core.symlinks', shell=True)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise
|
||||
except subprocess.CalledProcessError:
|
||||
# git returned non-zero status
|
||||
pass
|
||||
HAS_SYMLINKS = False
|
||||
if output.strip() == 'true':
|
||||
HAS_SYMLINKS = True
|
||||
return not HAS_SYMLINKS
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@skipIf(no_symlinks(), "Git missing 'core.symlinks=true' config")
|
||||
class InspectorCollectorTestCase(TestCase):
|
||||
'''
|
||||
Test inspectlib:collector:Inspector
|
||||
|
@ -6,7 +6,7 @@ import os
|
||||
|
||||
# Import Salt Testing libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch
|
||||
@ -14,6 +14,8 @@ from tests.support.mock import (
|
||||
# Import salt libs
|
||||
import salt.states.environ as envstate
|
||||
import salt.modules.environ as envmodule
|
||||
import salt.modules.reg
|
||||
import salt.utils
|
||||
|
||||
|
||||
class TestEnvironState(TestCase, LoaderModuleMockMixin):
|
||||
@ -22,17 +24,21 @@ class TestEnvironState(TestCase, LoaderModuleMockMixin):
|
||||
loader_globals = {
|
||||
'__env__': 'base',
|
||||
'__opts__': {'test': False},
|
||||
'__salt__': {'environ.setenv': envmodule.setenv}
|
||||
'__salt__': {
|
||||
'environ.setenv': envmodule.setenv,
|
||||
'reg.read_value': salt.modules.reg.read_value,
|
||||
}
|
||||
}
|
||||
return {envstate: loader_globals, envmodule: loader_globals}
|
||||
|
||||
def setUp(self):
|
||||
original_environ = os.environ.copy()
|
||||
os.environ = {'INITIAL': 'initial'}
|
||||
patcher = patch.dict(os.environ, {'INITIAL': 'initial'}, clear=True)
|
||||
patcher.start()
|
||||
|
||||
def reset_environ(original_environ):
|
||||
os.environ = original_environ
|
||||
self.addCleanup(reset_environ, original_environ)
|
||||
def reset_environ(patcher):
|
||||
patcher.stop()
|
||||
|
||||
self.addCleanup(reset_environ, patcher)
|
||||
|
||||
def test_setenv(self):
|
||||
'''test that a subsequent calls of setenv changes nothing'''
|
||||
@ -47,9 +53,10 @@ class TestEnvironState(TestCase, LoaderModuleMockMixin):
|
||||
ret = envstate.setenv('test', 'other')
|
||||
self.assertEqual(ret['changes'], {})
|
||||
|
||||
@skipIf(not salt.utils.is_windows(), 'Windows only')
|
||||
def test_setenv_permanent(self):
|
||||
with patch.dict(envmodule.__salt__, {'reg.set_value': MagicMock(), 'reg.delete_value': MagicMock()}), \
|
||||
patch('salt.utils.is_windows', MagicMock(return_value=True)):
|
||||
'''test that we can set perminent environment variables (requires pywin32)'''
|
||||
with patch.dict(envmodule.__salt__, {'reg.set_value': MagicMock(), 'reg.delete_value': MagicMock()}):
|
||||
ret = envstate.setenv('test', 'value', permanent=True)
|
||||
self.assertEqual(ret['changes'], {'test': 'value'})
|
||||
envmodule.__salt__['reg.set_value'].assert_called_with("HKCU", "Environment", 'test', 'value')
|
||||
@ -82,14 +89,20 @@ class TestEnvironState(TestCase, LoaderModuleMockMixin):
|
||||
'''test that ``clear_all`` option sets other values to '' '''
|
||||
ret = envstate.setenv('test', 'value', clear_all=True)
|
||||
self.assertEqual(ret['changes'], {'test': 'value', 'INITIAL': ''})
|
||||
self.assertEqual(envstate.os.environ, {'test': 'value', 'INITIAL': ''})
|
||||
if salt.utils.is_windows():
|
||||
self.assertEqual(envstate.os.environ, {'TEST': 'value', 'INITIAL': ''})
|
||||
else:
|
||||
self.assertEqual(envstate.os.environ, {'test': 'value', 'INITIAL': ''})
|
||||
|
||||
def test_setenv_clearall_with_unset(self):
|
||||
'''test that ``clear_all`` option combined with ``false_unsets``
|
||||
unsets other values from environment'''
|
||||
ret = envstate.setenv('test', 'value', false_unsets=True, clear_all=True)
|
||||
self.assertEqual(ret['changes'], {'test': 'value', 'INITIAL': None})
|
||||
self.assertEqual(envstate.os.environ, {'test': 'value'})
|
||||
if salt.utils.is_windows():
|
||||
self.assertEqual(envstate.os.environ, {'TEST': 'value'})
|
||||
else:
|
||||
self.assertEqual(envstate.os.environ, {'test': 'value'})
|
||||
|
||||
def test_setenv_unset_multi(self):
|
||||
'''test basically same things that above tests but with multiple values passed'''
|
||||
@ -100,12 +113,18 @@ class TestEnvironState(TestCase, LoaderModuleMockMixin):
|
||||
ret = envstate.setenv(
|
||||
'notimportant', {'test': False, 'foo': 'baz'}, false_unsets=True)
|
||||
self.assertEqual(ret['changes'], {'test': None, 'foo': 'baz'})
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'baz'})
|
||||
if salt.utils.is_windows():
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'FOO': 'baz'})
|
||||
else:
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'baz'})
|
||||
|
||||
with patch.dict(envstate.__salt__, {'reg.read_value': MagicMock()}):
|
||||
ret = envstate.setenv('notimportant', {'test': False, 'foo': 'bax'})
|
||||
self.assertEqual(ret['changes'], {'test': '', 'foo': 'bax'})
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'bax', 'test': ''})
|
||||
if salt.utils.is_windows():
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'FOO': 'bax', 'TEST': ''})
|
||||
else:
|
||||
self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'bax', 'test': ''})
|
||||
|
||||
def test_setenv_test_mode(self):
|
||||
'''test that imitating action returns good values'''
|
||||
|
@ -1843,3 +1843,36 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
|
||||
run_checks(test=True)
|
||||
run_checks(strptime_format=fake_strptime_format)
|
||||
run_checks(strptime_format=fake_strptime_format, test=True)
|
||||
|
||||
|
||||
class TestFindKeepFiles(TestCase):
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'Do not run on Windows')
|
||||
def test__find_keep_files_unix(self):
|
||||
keep = filestate._find_keep_files(
|
||||
'/test/parent_folder',
|
||||
['/test/parent_folder/meh.txt']
|
||||
)
|
||||
expected = [
|
||||
'/',
|
||||
'/test',
|
||||
'/test/parent_folder',
|
||||
'/test/parent_folder/meh.txt',
|
||||
]
|
||||
actual = sorted(list(keep))
|
||||
assert actual == expected, actual
|
||||
|
||||
@skipIf(not salt.utils.is_windows(), 'Only run on Windows')
|
||||
def test__find_keep_files_win32(self):
|
||||
keep = filestate._find_keep_files(
|
||||
'c:\\test\\parent_folder',
|
||||
['C:\\test\\parent_folder\\meh-2.txt']
|
||||
)
|
||||
expected = [
|
||||
'c:\\',
|
||||
'c:\\test',
|
||||
'c:\\test\\parent_folder',
|
||||
'c:\\test\\parent_folder\\meh-2.txt'
|
||||
]
|
||||
actual = sorted(list(keep))
|
||||
assert actual == expected, actual
|
||||
|
@ -41,17 +41,18 @@ class WinNetworkTestCase(TestCase, LoaderModuleMockMixin):
|
||||
' static, dhcp.'})
|
||||
self.assertDictEqual(win_network.managed('salt'), ret)
|
||||
|
||||
mock = MagicMock(return_value=False)
|
||||
mock_false = MagicMock(return_value=False)
|
||||
mock_true = MagicMock(return_value=True)
|
||||
mock1 = MagicMock(side_effect=[False, True, True, True, True, True,
|
||||
True])
|
||||
mock2 = MagicMock(side_effect=[False, True, True, {'salt': 'True'},
|
||||
{'salt': 'True'}])
|
||||
with patch.dict(win_network.__salt__, {"ip.is_enabled": mock,
|
||||
with patch.dict(win_network.__salt__, {"ip.is_enabled": mock_false,
|
||||
"ip.is_disabled": mock1,
|
||||
"ip.enable": mock,
|
||||
"ip.enable": mock_false,
|
||||
"ip.get_interface": mock2,
|
||||
"ip.set_dhcp_dns": mock,
|
||||
"ip.set_dhcp_ip": mock}):
|
||||
"ip.set_dhcp_dns": mock_false,
|
||||
"ip.set_dhcp_ip": mock_false}):
|
||||
ret.update({'comment': "Interface 'salt' is up to date."
|
||||
" (already disabled)", 'result': True})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
@ -66,52 +67,54 @@ class WinNetworkTestCase(TestCase, LoaderModuleMockMixin):
|
||||
dns_proto='static',
|
||||
ip_proto='static'),
|
||||
ret)
|
||||
mock = MagicMock(side_effect=['True', False, False, False, False,
|
||||
mock_false = MagicMock(side_effect=['True', False, False, False, False,
|
||||
False])
|
||||
with patch.object(win_network, '_validate', mock):
|
||||
ret.update({'comment': 'The following SLS configuration'
|
||||
' errors were detected: T r u e'})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='static',
|
||||
ip_proto='static'),
|
||||
ret)
|
||||
|
||||
ret.update({'comment': "Unable to get current"
|
||||
" configuration for interface 'salt'",
|
||||
'result': False})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dhcp',
|
||||
ip_proto='dhcp'),
|
||||
ret)
|
||||
with patch.dict(win_network.__salt__, {"ip.is_enabled": mock_true}):
|
||||
with patch.object(win_network, '_validate', mock_false):
|
||||
ret.update({'comment': 'The following SLS configuration'
|
||||
' errors were detected: T r u e'})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='static',
|
||||
ip_proto='static'),
|
||||
ret)
|
||||
|
||||
mock = MagicMock(side_effect=[False, [''],
|
||||
{'dns_proto': 'dhcp',
|
||||
'ip_proto': 'dhcp'},
|
||||
{'dns_proto': 'dhcp',
|
||||
'ip_proto': 'dhcp'}])
|
||||
ret.update({'comment': "Interface 'salt' is up to date.",
|
||||
'result': True})
|
||||
with patch.object(win_network, '_changes', mock):
|
||||
ret.update({'comment': "Unable to get current"
|
||||
" configuration for interface 'salt'",
|
||||
'result': False})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dhcp',
|
||||
ip_proto='dhcp'
|
||||
), ret)
|
||||
ip_proto='dhcp'),
|
||||
ret)
|
||||
|
||||
ret.update({'comment': "The following changes will be made"
|
||||
" to interface 'salt': ", 'result': None})
|
||||
with patch.dict(win_network.__opts__, {"test": True}):
|
||||
mock_false = MagicMock(side_effect=[False, [''],
|
||||
{'dns_proto': 'dhcp',
|
||||
'ip_proto': 'dhcp'},
|
||||
{'dns_proto': 'dhcp',
|
||||
'ip_proto': 'dhcp'}])
|
||||
ret.update({'comment': "Interface 'salt' is up to date.",
|
||||
'result': True})
|
||||
with patch.object(win_network, '_changes', mock_false):
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dh'
|
||||
'cp',
|
||||
dns_proto='dhcp',
|
||||
ip_proto='dhcp'
|
||||
), ret)
|
||||
|
||||
with patch.dict(win_network.__opts__, {"test": False}):
|
||||
ret.update({'comment': "Failed to set desired"
|
||||
" configuration settings for interface"
|
||||
" 'salt'", 'result': False})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dh'
|
||||
'cp',
|
||||
ip_proto='dhcp'
|
||||
), ret)
|
||||
ret.update({'comment': "The following changes will be made"
|
||||
" to interface 'salt': ", 'result': None})
|
||||
with patch.dict(win_network.__opts__, {"test": True}):
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dh'
|
||||
'cp',
|
||||
ip_proto='dhcp'
|
||||
), ret)
|
||||
|
||||
with patch.dict(win_network.__opts__, {"test": False}):
|
||||
ret.update({'comment': "Failed to set desired"
|
||||
" configuration settings for interface"
|
||||
" 'salt'", 'result': False})
|
||||
self.assertDictEqual(win_network.managed('salt',
|
||||
dns_proto='dh'
|
||||
'cp',
|
||||
ip_proto='dhcp'
|
||||
), ret)
|
||||
|
@ -13,7 +13,10 @@ from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON
|
||||
|
||||
# Import Salt libs
|
||||
from salt import client
|
||||
from salt.exceptions import EauthAuthenticationError, SaltInvocationError, SaltClientError
|
||||
import salt.utils
|
||||
from salt.exceptions import (
|
||||
EauthAuthenticationError, SaltInvocationError, SaltClientError, SaltReqTimeoutError
|
||||
)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@ -67,7 +70,13 @@ class LocalClientTestCase(TestCase,
|
||||
kwarg=None, tgt_type='list',
|
||||
ret='')
|
||||
|
||||
@skipIf(salt.utils.is_windows(), 'Not supported on Windows')
|
||||
def test_pub(self):
|
||||
'''
|
||||
Tests that the client cleanly returns when the publisher is not running
|
||||
|
||||
Note: Requires ZeroMQ's IPC transport which is not supported on windows.
|
||||
'''
|
||||
if self.get_config('minion')['transport'] != 'zeromq':
|
||||
self.skipTest('This test only works with ZeroMQ')
|
||||
# Make sure we cleanly return if the publisher isn't running
|
||||
@ -83,3 +92,27 @@ class LocalClientTestCase(TestCase,
|
||||
self.assertRaises(SaltInvocationError,
|
||||
self.client.pub,
|
||||
'non_existent_group', 'test.ping', tgt_type='nodegroup')
|
||||
|
||||
@skipIf(not salt.utils.is_windows(), 'Windows only test')
|
||||
def test_pub_win32(self):
|
||||
'''
|
||||
Tests that the client raises a timeout error when using ZeroMQ's TCP
|
||||
transport and publisher is not running.
|
||||
|
||||
Note: Requires ZeroMQ's TCP transport, this is only the default on Windows.
|
||||
'''
|
||||
if self.get_config('minion')['transport'] != 'zeromq':
|
||||
self.skipTest('This test only works with ZeroMQ')
|
||||
# Make sure we cleanly return if the publisher isn't running
|
||||
with patch('os.path.exists', return_value=False):
|
||||
self.assertRaises(SaltReqTimeoutError, lambda: self.client.pub('*', 'test.ping'))
|
||||
|
||||
# Check nodegroups behavior
|
||||
with patch('os.path.exists', return_value=True):
|
||||
with patch.dict(self.client.opts,
|
||||
{'nodegroups':
|
||||
{'group1': 'L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com'}}):
|
||||
# Do we raise an exception if the nodegroup can't be matched?
|
||||
self.assertRaises(SaltInvocationError,
|
||||
self.client.pub,
|
||||
'non_existent_group', 'test.ping', tgt_type='nodegroup')
|
||||
|
@ -10,6 +10,7 @@ integration.modules.test_data
|
||||
integration.modules.test_disk
|
||||
integration.modules.test_git
|
||||
integration.modules.test_grains
|
||||
integration.modules.test_groupadd
|
||||
integration.modules.test_hosts
|
||||
integration.modules.test_mine
|
||||
integration.modules.test_pillar
|
||||
|
Loading…
Reference in New Issue
Block a user