mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2018.3' into 2018.3
This commit is contained in:
commit
06bb2453ba
43 changed files with 451 additions and 192 deletions
|
@ -2402,6 +2402,12 @@ Master will not be returned to the Minion.
|
|||
------------------------------
|
||||
|
||||
.. versionadded:: 2014.1.0
|
||||
.. deprecated:: 2018.3.4
|
||||
This option is now ignored. Firstly, it only traversed
|
||||
:conf_master:`file_roots`, which means it did not work for the other
|
||||
fileserver backends. Secondly, since this option was added we have added
|
||||
caching to the code that traverses the file_roots (and gitfs, etc.), which
|
||||
greatly reduces the amount of traversal that is done.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ execution modules
|
|||
aix_group
|
||||
aliases
|
||||
alternatives
|
||||
ansiblegate
|
||||
apache
|
||||
apcups
|
||||
apf
|
||||
|
|
6
doc/ref/modules/all/salt.modules.ansiblegate.rst
Normal file
6
doc/ref/modules/all/salt.modules.ansiblegate.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
========================
|
||||
salt.modules.ansiblegate
|
||||
========================
|
||||
|
||||
.. automodule:: salt.modules.ansiblegate
|
||||
:members:
|
|
@ -42,6 +42,7 @@ pillar modules
|
|||
reclass_adapter
|
||||
redismod
|
||||
s3
|
||||
saltclass
|
||||
sql_base
|
||||
sqlcipher
|
||||
sqlite3
|
||||
|
@ -52,4 +53,3 @@ pillar modules
|
|||
venafi
|
||||
virtkey
|
||||
vmware_pillar
|
||||
|
||||
|
|
6
doc/ref/pillar/all/salt.pillar.saltclass.rst
Normal file
6
doc/ref/pillar/all/salt.pillar.saltclass.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
=====================
|
||||
salt.pillar.saltclass
|
||||
=====================
|
||||
|
||||
.. automodule:: salt.pillar.saltclass
|
||||
:members:
|
|
@ -13,6 +13,7 @@ state modules
|
|||
acme
|
||||
alias
|
||||
alternatives
|
||||
ansiblegate
|
||||
apache
|
||||
apache_conf
|
||||
apache_module
|
||||
|
|
6
doc/ref/states/all/salt.states.ansiblegate.rst
Normal file
6
doc/ref/states/all/salt.states.ansiblegate.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
=======================
|
||||
salt.states.ansiblegate
|
||||
=======================
|
||||
|
||||
.. automodule:: salt.states.ansiblegate
|
||||
:members:
|
|
@ -14,4 +14,5 @@ master tops modules
|
|||
ext_nodes
|
||||
mongo
|
||||
reclass_adapter
|
||||
saltclass
|
||||
varstack
|
||||
|
|
6
doc/ref/tops/all/salt.tops.saltclass.rst
Normal file
6
doc/ref/tops/all/salt.tops.saltclass.rst
Normal file
|
@ -0,0 +1,6 @@
|
|||
===================
|
||||
salt.tops.saltclass
|
||||
===================
|
||||
|
||||
.. automodule:: salt.tops.saltclass
|
||||
:members:
|
|
@ -114,6 +114,12 @@ Set up the provider cloud config at ``/etc/salt/cloud.providers`` or
|
|||
|
||||
driver: gce
|
||||
|
||||
.. note::
|
||||
|
||||
Empty strings as values for ``service_account_private_key`` and ``service_account_email_address``
|
||||
can be used on GCE instances. This will result in the service account assigned to the GCE instance
|
||||
being used.
|
||||
|
||||
.. note::
|
||||
|
||||
The value provided for ``project`` must not contain underscores or spaces and
|
||||
|
|
|
@ -35,8 +35,8 @@ NAPALM
|
|||
NAPALM (Network Automation and Programmability Abstraction Layer with
|
||||
Multivendor support) is an opensourced Python library that implements a set of
|
||||
functions to interact with different router vendor devices using a unified API.
|
||||
Begin vendor-agnostic simplifies the operations, as the configuration
|
||||
and the interaction with the network device does not rely on a particular vendor.
|
||||
Being vendor-agnostic simplifies operations, as the configuration and
|
||||
interaction with the network device does not rely on a particular vendor.
|
||||
|
||||
.. image:: /_static/napalm_logo.png
|
||||
|
||||
|
|
|
@ -4,3 +4,13 @@ In Progress: Salt 2017.7.9 Release Notes
|
|||
|
||||
Version 2017.7.9 is an **unreleased** bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
|
||||
This release is still in progress and has not been released yet.
|
||||
|
||||
Salt Cloud Features
|
||||
===================
|
||||
|
||||
GCE Driver
|
||||
----------
|
||||
The GCE salt cloud driver can now be used with GCE instance credentials by
|
||||
setting the configuration paramaters ``service_account_private_key`` and
|
||||
``service_account_private_email`` to an empty string.
|
||||
|
||||
|
|
|
@ -1182,7 +1182,7 @@ class LocalClient(object):
|
|||
# stop the iteration, since the jid is invalid
|
||||
raise StopIteration()
|
||||
except Exception as exc:
|
||||
log.warning('Returner unavailable: %s', exc)
|
||||
log.warning('Returner unavailable: %s', exc, exc_info_on_loglevel=logging.DEBUG)
|
||||
# Wait for the hosts to check in
|
||||
last_time = False
|
||||
# iterator for this job's return
|
||||
|
|
|
@ -928,7 +928,7 @@ def create_interface(call=None, kwargs=None): # pylint: disable=unused-argument
|
|||
)
|
||||
if pub_ip_data.ip_address: # pylint: disable=no-member
|
||||
ip_kwargs['public_ip_address'] = PublicIPAddress(
|
||||
six.text_type(pub_ip_data.id), # pylint: disable=no-member
|
||||
id=six.text_type(pub_ip_data.id), # pylint: disable=no-member
|
||||
)
|
||||
ip_configurations = [
|
||||
NetworkInterfaceIPConfiguration(
|
||||
|
|
|
@ -134,7 +134,8 @@ def __virtual__():
|
|||
|
||||
parameters = details['gce']
|
||||
pathname = os.path.expanduser(parameters['service_account_private_key'])
|
||||
if salt.utils.cloud.check_key_path_and_mode(
|
||||
# empty pathname will tell libcloud to use instance credentials
|
||||
if pathname and salt.utils.cloud.check_key_path_and_mode(
|
||||
provider, pathname
|
||||
) is False:
|
||||
return False
|
||||
|
|
|
@ -593,7 +593,7 @@ class SlackClient(object):
|
|||
Run each of them through ``get_configured_target(('foo', f), 'pillar.get')`` and confirm a valid target
|
||||
|
||||
'''
|
||||
# Default to targetting all minions with a type of glob
|
||||
# Default to targeting all minions with a type of glob
|
||||
null_target = {'target': '*', 'tgt_type': 'glob'}
|
||||
|
||||
def check_cmd_against_group(cmd):
|
||||
|
@ -627,14 +627,12 @@ class SlackClient(object):
|
|||
return checked
|
||||
return null_target
|
||||
|
||||
|
||||
# emulate the yaml_out output formatter. It relies on a global __opts__ object which we can't
|
||||
# obviously pass in
|
||||
|
||||
def format_return_text(self, data, function, **kwargs): # pylint: disable=unused-argument
|
||||
'''
|
||||
Print out YAML using the block mode
|
||||
'''
|
||||
# emulate the yaml_out output formatter. It relies on a global __opts__ object which
|
||||
# we can't obviously pass in
|
||||
try:
|
||||
# Format results from state runs with highstate output
|
||||
if function.startswith('state'):
|
||||
|
|
|
@ -61,7 +61,7 @@ def get_file_client(opts, pillar=False):
|
|||
return {
|
||||
'remote': RemoteClient,
|
||||
'local': FSClient,
|
||||
'pillar': LocalClient,
|
||||
'pillar': PillarClient,
|
||||
}.get(client, RemoteClient)(opts)
|
||||
|
||||
|
||||
|
@ -347,58 +347,17 @@ class Client(object):
|
|||
Return a list of all available sls modules on the master for a given
|
||||
environment
|
||||
'''
|
||||
|
||||
limit_traversal = self.opts.get('fileserver_limit_traversal', False)
|
||||
states = []
|
||||
|
||||
if limit_traversal:
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
log.warning(
|
||||
'During an attempt to list states for saltenv \'%s\', '
|
||||
'the environment could not be found in the configured '
|
||||
'file roots', saltenv
|
||||
)
|
||||
return states
|
||||
for path in self.opts['file_roots'][saltenv]:
|
||||
for root, dirs, files in os.walk(path, topdown=True): # future lint: disable=blacklisted-function
|
||||
root = salt.utils.data.decode(root)
|
||||
files = salt.utils.data.decode(files)
|
||||
log.debug(
|
||||
'Searching for states in dirs %s and files %s',
|
||||
salt.utils.data.decode(dirs), files
|
||||
)
|
||||
if not [filename.endswith('.sls') for filename in files]:
|
||||
# Use shallow copy so we don't disturb the memory used
|
||||
# by os.walk. Otherwise this breaks!
|
||||
del dirs[:]
|
||||
else:
|
||||
for found_file in files:
|
||||
stripped_root = os.path.relpath(root, path)
|
||||
if salt.utils.platform.is_windows():
|
||||
stripped_root = stripped_root.replace('\\', '/')
|
||||
stripped_root = stripped_root.replace('/', '.')
|
||||
if found_file.endswith(('.sls')):
|
||||
if found_file.endswith('init.sls'):
|
||||
if stripped_root.endswith('.'):
|
||||
stripped_root = stripped_root.rstrip('.')
|
||||
states.append(stripped_root)
|
||||
else:
|
||||
if not stripped_root.endswith('.'):
|
||||
stripped_root += '.'
|
||||
if stripped_root.startswith('.'):
|
||||
stripped_root = stripped_root.lstrip('.')
|
||||
states.append(stripped_root + found_file[:-4])
|
||||
else:
|
||||
for path in self.file_list(saltenv):
|
||||
if salt.utils.platform.is_windows():
|
||||
path = path.replace('\\', '/')
|
||||
if path.endswith('.sls'):
|
||||
# is an sls module!
|
||||
if path.endswith('/init.sls'):
|
||||
states.append(path.replace('/', '.')[:-9])
|
||||
else:
|
||||
states.append(path.replace('/', '.')[:-4])
|
||||
return states
|
||||
states = set()
|
||||
for path in self.file_list(saltenv):
|
||||
if salt.utils.platform.is_windows():
|
||||
path = path.replace('\\', '/')
|
||||
if path.endswith('.sls'):
|
||||
# is an sls module!
|
||||
if path.endswith('/init.sls'):
|
||||
states.add(path.replace('/', '.')[:-9])
|
||||
else:
|
||||
states.add(path.replace('/', '.')[:-4])
|
||||
return sorted(states)
|
||||
|
||||
def get_state(self, sls, saltenv, cachedir=None):
|
||||
'''
|
||||
|
@ -844,13 +803,10 @@ class Client(object):
|
|||
)
|
||||
|
||||
|
||||
class LocalClient(Client):
|
||||
class PillarClient(Client):
|
||||
'''
|
||||
Use the local_roots option to parse a local file root
|
||||
Used by pillar to handle fileclient requests
|
||||
'''
|
||||
def __init__(self, opts):
|
||||
Client.__init__(self, opts)
|
||||
|
||||
def _find_file(self, path, saltenv='base'):
|
||||
'''
|
||||
Locate the file path
|
||||
|
@ -858,12 +814,12 @@ class LocalClient(Client):
|
|||
fnd = {'path': '',
|
||||
'rel': ''}
|
||||
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
if saltenv not in self.opts['pillar_roots']:
|
||||
return fnd
|
||||
if salt.utils.url.is_escaped(path):
|
||||
# The path arguments are escaped
|
||||
path = salt.utils.url.unescape(path)
|
||||
for root in self.opts['file_roots'][saltenv]:
|
||||
for root in self.opts['pillar_roots'][saltenv]:
|
||||
full = os.path.join(root, path)
|
||||
if os.path.isfile(full):
|
||||
fnd['path'] = full
|
||||
|
@ -896,10 +852,10 @@ class LocalClient(Client):
|
|||
with optional relative prefix path to limit directory traversal
|
||||
'''
|
||||
ret = []
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
if saltenv not in self.opts['pillar_roots']:
|
||||
return ret
|
||||
prefix = prefix.strip('/')
|
||||
for path in self.opts['file_roots'][saltenv]:
|
||||
for path in self.opts['pillar_roots'][saltenv]:
|
||||
for root, dirs, files in salt.utils.path.os_walk(
|
||||
os.path.join(path, prefix), followlinks=True
|
||||
):
|
||||
|
@ -912,14 +868,14 @@ class LocalClient(Client):
|
|||
|
||||
def file_list_emptydirs(self, saltenv='base', prefix=''):
|
||||
'''
|
||||
List the empty dirs in the file_roots
|
||||
List the empty dirs in the pillar_roots
|
||||
with optional relative prefix path to limit directory traversal
|
||||
'''
|
||||
ret = []
|
||||
prefix = prefix.strip('/')
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
if saltenv not in self.opts['pillar_roots']:
|
||||
return ret
|
||||
for path in self.opts['file_roots'][saltenv]:
|
||||
for path in self.opts['pillar_roots'][saltenv]:
|
||||
for root, dirs, files in salt.utils.path.os_walk(
|
||||
os.path.join(path, prefix), followlinks=True
|
||||
):
|
||||
|
@ -931,14 +887,14 @@ class LocalClient(Client):
|
|||
|
||||
def dir_list(self, saltenv='base', prefix=''):
|
||||
'''
|
||||
List the dirs in the file_roots
|
||||
List the dirs in the pillar_roots
|
||||
with optional relative prefix path to limit directory traversal
|
||||
'''
|
||||
ret = []
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
if saltenv not in self.opts['pillar_roots']:
|
||||
return ret
|
||||
prefix = prefix.strip('/')
|
||||
for path in self.opts['file_roots'][saltenv]:
|
||||
for path in self.opts['pillar_roots'][saltenv]:
|
||||
for root, dirs, files in salt.utils.path.os_walk(
|
||||
os.path.join(path, prefix), followlinks=True
|
||||
):
|
||||
|
@ -965,7 +921,7 @@ class LocalClient(Client):
|
|||
|
||||
def hash_file(self, path, saltenv='base'):
|
||||
'''
|
||||
Return the hash of a file, to get the hash of a file in the file_roots
|
||||
Return the hash of a file, to get the hash of a file in the pillar_roots
|
||||
prepend the path with salt://<file on server> otherwise, prepend the
|
||||
file with / for a local file.
|
||||
'''
|
||||
|
@ -988,7 +944,7 @@ class LocalClient(Client):
|
|||
|
||||
def hash_and_stat_file(self, path, saltenv='base'):
|
||||
'''
|
||||
Return the hash of a file, to get the hash of a file in the file_roots
|
||||
Return the hash of a file, to get the hash of a file in the pillar_roots
|
||||
prepend the path with salt://<file on server> otherwise, prepend the
|
||||
file with / for a local file.
|
||||
|
||||
|
@ -1034,7 +990,7 @@ class LocalClient(Client):
|
|||
Return the available environments
|
||||
'''
|
||||
ret = []
|
||||
for saltenv in self.opts['file_roots']:
|
||||
for saltenv in self.opts['pillar_roots']:
|
||||
ret.append(saltenv)
|
||||
return ret
|
||||
|
||||
|
@ -1428,6 +1384,11 @@ class FSClient(RemoteClient):
|
|||
self.auth = DumbAuth()
|
||||
|
||||
|
||||
# Provide backward compatibility for anyone directly using LocalClient (but no
|
||||
# one should be doing this).
|
||||
LocalClient = FSClient
|
||||
|
||||
|
||||
class DumbAuth(object):
|
||||
'''
|
||||
The dumbauth class is used to stub out auth calls fired from the FSClient
|
||||
|
|
|
@ -131,7 +131,10 @@ def check_file_list_cache(opts, form, list_cache, w_lock):
|
|||
if os.path.exists(list_cache):
|
||||
# calculate filelist age is possible
|
||||
cache_stat = os.stat(list_cache)
|
||||
age = time.time() - cache_stat.st_mtime
|
||||
# st_time can have a greater precision than time, removing
|
||||
# float precision makes sure age will never be a negative
|
||||
# number.
|
||||
age = int(time.time()) - int(cache_stat.st_mtime)
|
||||
else:
|
||||
# if filelist does not exists yet, mark it as expired
|
||||
age = opts.get('fileserver_list_cache_time', 20) + 1
|
||||
|
|
|
@ -1589,7 +1589,7 @@ class Minion(MinionBase):
|
|||
|
||||
sdata = {'pid': os.getpid()}
|
||||
sdata.update(data)
|
||||
log.info('Starting a new job with PID %s', sdata['pid'])
|
||||
log.info('Starting a new job %s with PID %s', data['jid'], sdata['pid'])
|
||||
with salt.utils.files.fopen(fn_, 'w+b') as fp_:
|
||||
fp_.write(minion_instance.serial.dumps(sdata))
|
||||
ret = {'success': False}
|
||||
|
|
|
@ -214,7 +214,7 @@ def get(consul_url=None, key=None, token=None, recurse=False, decode=False, raw=
|
|||
if ret['res']:
|
||||
if decode:
|
||||
for item in ret['data']:
|
||||
if item['Value'] != None:
|
||||
if item['Value'] is not None:
|
||||
item['Value'] = base64.b64decode(item['Value'])
|
||||
else:
|
||||
item['Value'] = ""
|
||||
|
|
|
@ -420,7 +420,9 @@ def get_url(path, dest='', saltenv='base', makedirs=False, source_hash=None):
|
|||
result = _client().get_url(
|
||||
path, None, makedirs, saltenv, no_cache=True, source_hash=source_hash)
|
||||
if not result:
|
||||
log.error('Unable to fetch file %s from saltenv %s.', path, saltenv)
|
||||
log.error('Unable to fetch file %s from saltenv %s.',
|
||||
salt.utils.url.redact_http_basic_auth(path),
|
||||
saltenv)
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -4119,7 +4119,7 @@ def get_managed(
|
|||
msg = (
|
||||
'Unable to verify upstream hash of source file {0}, '
|
||||
'please set source_hash or set skip_verify to True'
|
||||
.format(source)
|
||||
.format(salt.utils.url.redact_http_basic_auth(source))
|
||||
)
|
||||
return '', {}, msg
|
||||
|
||||
|
@ -4151,12 +4151,14 @@ def get_managed(
|
|||
except Exception as exc:
|
||||
# A 404 or other error code may raise an exception, catch it
|
||||
# and return a comment that will fail the calling state.
|
||||
return '', {}, 'Failed to cache {0}: {1}'.format(source, exc)
|
||||
_source = salt.utils.url.redact_http_basic_auth(source)
|
||||
return '', {}, 'Failed to cache {0}: {1}'.format(_source, exc)
|
||||
|
||||
# If cache failed, sfn will be False, so do a truth check on sfn first
|
||||
# as invoking os.path.exists() on a bool raises a TypeError.
|
||||
if not sfn or not os.path.exists(sfn):
|
||||
return sfn, {}, 'Source file \'{0}\' not found'.format(source)
|
||||
_source = salt.utils.url.redact_http_basic_auth(source)
|
||||
return sfn, {}, 'Source file \'{0}\' not found'.format(_source)
|
||||
if sfn == name:
|
||||
raise SaltInvocationError(
|
||||
'Source file cannot be the same as destination'
|
||||
|
|
|
@ -1035,11 +1035,8 @@ def _parse_conf(conf_file=None, in_mem=False, family='ipv4'):
|
|||
if args[-1].startswith('-'):
|
||||
args.append('')
|
||||
parsed_args = []
|
||||
if sys.version.startswith('2.6'):
|
||||
(opts, leftover_args) = parser.parse_args(args)
|
||||
parsed_args = vars(opts)
|
||||
else:
|
||||
parsed_args = vars(parser.parse_args(args))
|
||||
opts, _ = parser.parse_known_args(args)
|
||||
parsed_args = vars(opts)
|
||||
ret_args = {}
|
||||
chain = parsed_args['append']
|
||||
for arg in parsed_args:
|
||||
|
|
|
@ -4689,7 +4689,7 @@ def get_pid(name, path=None):
|
|||
if name not in list_(limit='running', path=path):
|
||||
raise CommandExecutionError('Container {0} is not running, can\'t determine PID'.format(name))
|
||||
info = __salt__['cmd.run']('lxc-info -n {0}'.format(name)).split("\n")
|
||||
pid = [line.split(':')[1].strip() for line in info if re.match(r'\s*PID', line) != None][0]
|
||||
pid = [line.split(':')[1].strip() for line in info if re.match(r'\s*PID', line) is not None][0]
|
||||
return pid
|
||||
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ def _atrun_enabled():
|
|||
Check to see if atrun is enabled on the system
|
||||
'''
|
||||
name = 'com.apple.atrun'
|
||||
services = salt.utils.mac_utils.available_services()
|
||||
services = __utils__['mac_utils.available_services']()
|
||||
label = None
|
||||
|
||||
if name in services:
|
||||
|
|
|
@ -1989,17 +1989,20 @@ def processlist(**connection_args):
|
|||
"SHOW FULL PROCESSLIST".
|
||||
|
||||
Returns: a list of dicts, with each dict representing a process:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{'Command': 'Query',
|
||||
'Host': 'localhost',
|
||||
'Id': 39,
|
||||
'Info': 'SHOW FULL PROCESSLIST',
|
||||
'Rows_examined': 0,
|
||||
'Rows_read': 1,
|
||||
'Rows_sent': 0,
|
||||
'State': None,
|
||||
'Time': 0,
|
||||
'User': 'root',
|
||||
'db': 'mysql'}
|
||||
'Host': 'localhost',
|
||||
'Id': 39,
|
||||
'Info': 'SHOW FULL PROCESSLIST',
|
||||
'Rows_examined': 0,
|
||||
'Rows_read': 1,
|
||||
'Rows_sent': 0,
|
||||
'State': None,
|
||||
'Time': 0,
|
||||
'User': 'root',
|
||||
'db': 'mysql'}
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
|
|
@ -4337,7 +4337,7 @@ def _writeAdminTemplateRegPolFile(admtemplate_data,
|
|||
adml_policy_resources=None,
|
||||
display_language='en-US',
|
||||
registry_class='Machine'):
|
||||
u'''
|
||||
r'''
|
||||
helper function to prep/write adm template data to the Registry.pol file
|
||||
|
||||
each file begins with REGFILE_SIGNATURE (u'\u5250\u6765') and
|
||||
|
|
|
@ -699,7 +699,6 @@ def refresh_db(**kwargs):
|
|||
include_pat='*.sls',
|
||||
exclude_pat=r'E@\/\..*?\/' # Exclude all hidden directories (.git)
|
||||
)
|
||||
|
||||
return genrepo(saltenv=saltenv, verbose=verbose, failhard=failhard)
|
||||
|
||||
|
||||
|
|
|
@ -345,8 +345,6 @@ class Pillar(object):
|
|||
if pillarenv is None:
|
||||
if opts.get('pillarenv_from_saltenv', False):
|
||||
opts['pillarenv'] = saltenv
|
||||
# Store the file_roots path so we can restore later. Issue 5449
|
||||
self.actual_file_roots = opts['file_roots']
|
||||
# use the local file client
|
||||
self.opts = self.__gen_opts(opts, grains, saltenv=saltenv, pillarenv=pillarenv)
|
||||
self.saltenv = saltenv
|
||||
|
@ -369,9 +367,6 @@ class Pillar(object):
|
|||
self.matcher = salt.minion.Matcher(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions)
|
||||
ext_pillar_opts = copy.deepcopy(self.opts)
|
||||
# Fix self.opts['file_roots'] so that ext_pillars know the real
|
||||
# location of file_roots. Issue 5951
|
||||
ext_pillar_opts['file_roots'] = self.actual_file_roots
|
||||
# Keep the incoming opts ID intact, ie, the master id
|
||||
if 'id' in opts:
|
||||
ext_pillar_opts['id'] = opts['id']
|
||||
|
@ -438,7 +433,6 @@ class Pillar(object):
|
|||
The options need to be altered to conform to the file client
|
||||
'''
|
||||
opts = copy.deepcopy(opts_in)
|
||||
opts['file_roots'] = opts['pillar_roots']
|
||||
opts['file_client'] = 'local'
|
||||
if not grains:
|
||||
opts['grains'] = {}
|
||||
|
@ -463,15 +457,15 @@ class Pillar(object):
|
|||
opts['ext_pillar'].append(self.ext)
|
||||
else:
|
||||
opts['ext_pillar'] = [self.ext]
|
||||
if '__env__' in opts['file_roots']:
|
||||
if '__env__' in opts['pillar_roots']:
|
||||
env = opts.get('pillarenv') or opts.get('saltenv') or 'base'
|
||||
if env not in opts['file_roots']:
|
||||
if env not in opts['pillar_roots']:
|
||||
log.debug("pillar environment '%s' maps to __env__ pillar_roots directory", env)
|
||||
opts['file_roots'][env] = opts['file_roots'].pop('__env__')
|
||||
opts['pillar_roots'][env] = opts['pillar_roots'].pop('__env__')
|
||||
else:
|
||||
log.debug("pillar_roots __env__ ignored (environment '%s' found in pillar_roots)",
|
||||
env)
|
||||
opts['file_roots'].pop('__env__')
|
||||
opts['pillar_roots'].pop('__env__')
|
||||
return opts
|
||||
|
||||
def _get_envs(self):
|
||||
|
@ -479,8 +473,8 @@ class Pillar(object):
|
|||
Pull the file server environments out of the master options
|
||||
'''
|
||||
envs = set(['base'])
|
||||
if 'file_roots' in self.opts:
|
||||
envs.update(list(self.opts['file_roots']))
|
||||
if 'pillar_roots' in self.opts:
|
||||
envs.update(list(self.opts['pillar_roots']))
|
||||
return envs
|
||||
|
||||
def get_tops(self):
|
||||
|
@ -496,11 +490,11 @@ class Pillar(object):
|
|||
if self.opts['pillarenv']:
|
||||
# If the specified pillarenv is not present in the available
|
||||
# pillar environments, do not cache the pillar top file.
|
||||
if self.opts['pillarenv'] not in self.opts['file_roots']:
|
||||
if self.opts['pillarenv'] not in self.opts['pillar_roots']:
|
||||
log.debug(
|
||||
'pillarenv \'%s\' not found in the configured pillar '
|
||||
'environments (%s)',
|
||||
self.opts['pillarenv'], ', '.join(self.opts['file_roots'])
|
||||
self.opts['pillarenv'], ', '.join(self.opts['pillar_roots'])
|
||||
)
|
||||
else:
|
||||
top = self.client.cache_file(self.opts['state_top'], self.opts['pillarenv'])
|
||||
|
@ -1010,8 +1004,6 @@ class Pillar(object):
|
|||
mopts = dict(self.opts)
|
||||
if 'grains' in mopts:
|
||||
mopts.pop('grains')
|
||||
# Restore the actual file_roots path. Issue 5449
|
||||
mopts['file_roots'] = self.actual_file_roots
|
||||
mopts['saltversion'] = __version__
|
||||
pillar['master'] = mopts
|
||||
if 'pillar' in self.opts and self.opts.get('ssh_merge_pillar', False):
|
||||
|
@ -1038,10 +1030,6 @@ class Pillar(object):
|
|||
if decrypt_errors:
|
||||
pillar.setdefault('_errors', []).extend(decrypt_errors)
|
||||
|
||||
# Reset the file_roots for the renderers
|
||||
for mod_name in sys.modules:
|
||||
if mod_name.startswith('salt.loaded.int.render.'):
|
||||
sys.modules[mod_name].__opts__['file_roots'] = self.actual_file_roots
|
||||
return pillar
|
||||
|
||||
def decrypt_pillar(self, pillar):
|
||||
|
|
|
@ -156,6 +156,7 @@ import salt.utils.files
|
|||
import salt.utils.minions
|
||||
import salt.utils.path
|
||||
import salt.utils.stringio
|
||||
import salt.utils.stringutils
|
||||
import salt.template
|
||||
from salt.ext import six
|
||||
|
||||
|
@ -251,7 +252,7 @@ def _construct_pillar(top_dir,
|
|||
else:
|
||||
data = contents
|
||||
if template is True:
|
||||
data = salt.template.compile_template_str(template=contents,
|
||||
data = salt.template.compile_template_str(template=salt.utils.stringutils.to_unicode(contents),
|
||||
renderers=renderers,
|
||||
default=render_default,
|
||||
blacklist=renderer_blacklist,
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
SaltClass Pillar Module
|
||||
=======================
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ext_pillar:
|
||||
- saltclass:
|
||||
- path: /srv/saltclass
|
||||
ext_pillar:
|
||||
- saltclass:
|
||||
- path: /srv/saltclass
|
||||
|
||||
For additional configuration instructions, see the :mod:`saltclass <salt.tops.saltclass>` module
|
||||
'''
|
||||
|
||||
# import python libs
|
||||
|
|
|
@ -48,7 +48,7 @@ def present(name,
|
|||
The keyId or keyIds to add to the GPG keychain.
|
||||
|
||||
user
|
||||
Add GPG keys to the user's keychain
|
||||
Add GPG keys to the specified user's keychain
|
||||
|
||||
keyserver
|
||||
The keyserver to retrieve the keys from.
|
||||
|
@ -151,7 +151,7 @@ def absent(name,
|
|||
The keyId or keyIds to add to the GPG keychain.
|
||||
|
||||
user
|
||||
Add GPG keys to the user's keychain
|
||||
Remove GPG keys from the specified user's keychain
|
||||
|
||||
gnupghome
|
||||
Override GNUPG Home directory
|
||||
|
|
|
@ -42,7 +42,7 @@ def __virtual__():
|
|||
|
||||
def pv_present(name, **kwargs):
|
||||
'''
|
||||
Set a physical device to be used as an LVM physical volume
|
||||
Set a Physical Device to be used as an LVM Physical Volume
|
||||
|
||||
name
|
||||
The device name to initialize.
|
||||
|
@ -106,13 +106,13 @@ def pv_absent(name):
|
|||
|
||||
def vg_present(name, devices=None, **kwargs):
|
||||
'''
|
||||
Create an LVM volume group
|
||||
Create an LVM Volume Group
|
||||
|
||||
name
|
||||
The volume group name to create
|
||||
The Volume Group name to create
|
||||
|
||||
devices
|
||||
A list of devices that will be added to the volume group
|
||||
A list of devices that will be added to the Volume Group
|
||||
|
||||
kwargs
|
||||
Any supported options to vgcreate. See
|
||||
|
@ -214,16 +214,16 @@ def lv_present(name,
|
|||
force=False,
|
||||
**kwargs):
|
||||
'''
|
||||
Create a new logical volume
|
||||
Create a new Logical Volume
|
||||
|
||||
name
|
||||
The name of the logical volume
|
||||
The name of the Logical Volume
|
||||
|
||||
vgname
|
||||
The volume group name for this logical volume
|
||||
The name of the Volume Group on which the Logical Volume resides
|
||||
|
||||
size
|
||||
The initial size of the logical volume
|
||||
The initial size of the Logical Volume
|
||||
|
||||
extents
|
||||
The number of logical extents to allocate
|
||||
|
@ -232,7 +232,7 @@ def lv_present(name,
|
|||
The name of the snapshot
|
||||
|
||||
pv
|
||||
The physical volume to use
|
||||
The Physical Volume to use
|
||||
|
||||
kwargs
|
||||
Any supported options to lvcreate. See
|
||||
|
@ -241,10 +241,10 @@ def lv_present(name,
|
|||
.. versionadded:: to_complete
|
||||
|
||||
thinvolume
|
||||
Logical volume is thinly provisioned
|
||||
Logical Volume is thinly provisioned
|
||||
|
||||
thinpool
|
||||
Logical volume is a thin pool
|
||||
Logical Volume is a thin pool
|
||||
|
||||
.. versionadded:: 2018.3.0
|
||||
|
||||
|
@ -297,13 +297,13 @@ def lv_present(name,
|
|||
|
||||
def lv_absent(name, vgname=None):
|
||||
'''
|
||||
Remove a given existing logical volume from a named existing volume group
|
||||
Remove a given existing Logical Volume from a named existing volume group
|
||||
|
||||
name
|
||||
The logical volume to remove
|
||||
The Logical Volume to remove
|
||||
|
||||
vgname
|
||||
The volume group name
|
||||
The name of the Volume Group on which the Logical Volume resides
|
||||
'''
|
||||
ret = {'changes': {},
|
||||
'comment': '',
|
||||
|
|
|
@ -26,7 +26,7 @@ def absent(name, user=None, signal=None):
|
|||
The pattern to match.
|
||||
|
||||
user
|
||||
The user process belongs
|
||||
The user to which the process belongs
|
||||
|
||||
signal
|
||||
Signal to send to the process(es).
|
||||
|
|
|
@ -55,7 +55,7 @@ def managed(name, port, services=None, user=None, password=None, bypass_domains=
|
|||
The username to use for the proxy server if required
|
||||
|
||||
password
|
||||
The password to use if required by the server
|
||||
The password to use for the proxy server if required
|
||||
|
||||
bypass_domains
|
||||
An array of the domains that should bypass the proxy
|
||||
|
|
|
@ -334,7 +334,7 @@ def _check_key_type(key_str, key_type=None):
|
|||
value = __salt__['pillar.get'](key_str, None)
|
||||
if value is None:
|
||||
return None
|
||||
elif type(value) is not key_type and key_type is not None:
|
||||
elif key_type is not None and not isinstance(value, key_type):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
@ -415,7 +415,7 @@ def check_pillar(name,
|
|||
checks[int] = integer
|
||||
# those should be str:
|
||||
string = _if_str_then_list(string)
|
||||
checks[str] = string
|
||||
checks[six.string_types] = string
|
||||
# those should be list:
|
||||
listing = _if_str_then_list(listing)
|
||||
checks[list] = listing
|
||||
|
|
|
@ -1,11 +1,211 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
SaltClass master_tops Module
|
||||
r'''
|
||||
Saltclass Configuration
|
||||
=======================
|
||||
|
||||
.. code-block:: yaml
|
||||
master_tops:
|
||||
saltclass:
|
||||
path: /srv/saltclass
|
||||
|
||||
master_tops:
|
||||
saltclass:
|
||||
path: /srv/saltclass
|
||||
|
||||
Description
|
||||
===========
|
||||
|
||||
This module clones the behaviour of reclass (http://reclass.pantsfullofunix.net/),
|
||||
without the need of an external app, and add several features to improve flexibility.
|
||||
Saltclass lets you define your nodes from simple ``yaml`` files (``.yml``) through
|
||||
hierarchical class inheritance with the possibility to override pillars down the tree.
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- Define your nodes through hierarchical class inheritance
|
||||
- Reuse your reclass datas with minimal modifications
|
||||
- applications => states
|
||||
- parameters => pillars
|
||||
- Use Jinja templating in your yaml definitions
|
||||
- Access to the following Salt objects in Jinja
|
||||
- ``__opts__``
|
||||
- ``__salt__``
|
||||
- ``__grains__``
|
||||
- ``__pillars__``
|
||||
- ``minion_id``
|
||||
- Chose how to merge or override your lists using ^ character (see examples)
|
||||
- Expand variables ${} with possibility to escape them if needed \${} (see examples)
|
||||
- Ignores missing node/class and will simply return empty without breaking the pillar module completely - will be logged
|
||||
|
||||
An example subset of datas is available here: http://git.mauras.ch/salt/saltclass/src/master/examples
|
||||
|
||||
========================== ===========
|
||||
Terms usable in yaml files Description
|
||||
========================== ===========
|
||||
classes A list of classes that will be processed in order
|
||||
states A list of states that will be returned by master_tops function
|
||||
pillars A yaml dictionnary that will be returned by the ext_pillar function
|
||||
environment Node saltenv that will be used by master_tops
|
||||
========================== ===========
|
||||
|
||||
A class consists of:
|
||||
|
||||
- zero or more parent classes
|
||||
- zero or more states
|
||||
- any number of pillars
|
||||
|
||||
A child class can override pillars from a parent class.
|
||||
A node definition is a class in itself with an added ``environment`` parameter for ``saltenv`` definition.
|
||||
|
||||
Class names
|
||||
===========
|
||||
|
||||
Class names mimic salt way of defining states and pillar files.
|
||||
This means that ``default.users`` class name will correspond to one of these:
|
||||
|
||||
- ``<saltclass_path>/classes/default/users.yml``
|
||||
- ``<saltclass_path>/classes/default/users/init.yml``
|
||||
|
||||
Saltclass file hierachy
|
||||
=======================
|
||||
|
||||
A saltclass tree would look like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
<saltclass_path>
|
||||
├── classes
|
||||
│ ├── app
|
||||
│ │ ├── borgbackup.yml
|
||||
│ │ └── ssh
|
||||
│ │ └── server.yml
|
||||
│ ├── default
|
||||
│ │ ├── init.yml
|
||||
│ │ ├── motd.yml
|
||||
│ │ └── users.yml
|
||||
│ ├── roles
|
||||
│ │ ├── app.yml
|
||||
│ │ └── nginx
|
||||
│ │ ├── init.yml
|
||||
│ │ └── server.yml
|
||||
│ └── subsidiaries
|
||||
│ ├── gnv.yml
|
||||
│ ├── qls.yml
|
||||
│ └── zrh.yml
|
||||
└── nodes
|
||||
├── geneva
|
||||
│ └── gnv.node1.yml
|
||||
├── lausanne
|
||||
│ ├── qls.node1.yml
|
||||
│ └── qls.node2.yml
|
||||
├── node127.yml
|
||||
└── zurich
|
||||
├── zrh.node1.yml
|
||||
├── zrh.node2.yml
|
||||
└── zrh.node3.yml
|
||||
|
||||
|
||||
Saltclass Examples
|
||||
==================
|
||||
|
||||
``<saltclass_path>/nodes/lausanne/qls.node1.yml``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
environment: base
|
||||
|
||||
classes:
|
||||
{% for class in ['default'] %}
|
||||
- {{ class }}
|
||||
{% endfor %}
|
||||
- subsidiaries.{{ __grains__['id'].split('.')[0] }}
|
||||
|
||||
``<saltclass_path>/classes/default/init.yml``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
classes:
|
||||
- default.users
|
||||
- default.motd
|
||||
|
||||
states:
|
||||
- openssh
|
||||
|
||||
pillars:
|
||||
default:
|
||||
network:
|
||||
dns:
|
||||
srv1: 192.168.0.1
|
||||
srv2: 192.168.0.2
|
||||
domain: example.com
|
||||
ntp:
|
||||
srv1: 192.168.10.10
|
||||
srv2: 192.168.10.20
|
||||
|
||||
``<saltclass_path>/classes/subsidiaries/gnv.yml``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillars:
|
||||
default:
|
||||
network:
|
||||
sub: Geneva
|
||||
dns:
|
||||
srv1: 10.20.0.1
|
||||
srv2: 10.20.0.2
|
||||
srv3: 192.168.1.1
|
||||
domain: gnv.example.com
|
||||
users:
|
||||
adm1:
|
||||
uid: 1210
|
||||
gid: 1210
|
||||
gecos: 'Super user admin1'
|
||||
homedir: /srv/app/adm1
|
||||
adm3:
|
||||
uid: 1203
|
||||
gid: 1203
|
||||
gecos: 'Super user adm
|
||||
|
||||
Variable expansions
|
||||
===================
|
||||
|
||||
Escaped variables are rendered as is: ``${test}``
|
||||
|
||||
Missing variables are rendered as is: ``${net:dns:srv2}``
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillars:
|
||||
app:
|
||||
config:
|
||||
dns:
|
||||
srv1: ${default:network:dns:srv1}
|
||||
srv2: ${net:dns:srv2}
|
||||
uri: https://application.domain/call?\${test}
|
||||
prod_parameters:
|
||||
- p1
|
||||
- p2
|
||||
- p3
|
||||
pkg:
|
||||
- app-core
|
||||
- app-backend
|
||||
|
||||
List override
|
||||
=============
|
||||
|
||||
Not using ``^`` as the first entry will simply merge the lists
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillars:
|
||||
app:
|
||||
pkg:
|
||||
- ^
|
||||
- app-frontend
|
||||
|
||||
|
||||
.. note:: **Known limitation**
|
||||
|
||||
Currently you can't have both a variable and an escaped variable in the same string as the
|
||||
escaped one will not be correctly rendered - '\${xx}' will stay as is instead of being rendered as '${xx}'
|
||||
'''
|
||||
|
||||
# import python libs
|
||||
|
|
|
@ -59,16 +59,16 @@ class SaltCacheLoader(BaseLoader):
|
|||
self.opts = opts
|
||||
self.saltenv = saltenv
|
||||
self.encoding = encoding
|
||||
if self.opts['file_roots'] is self.opts['pillar_roots']:
|
||||
if saltenv not in self.opts['file_roots']:
|
||||
self.pillar_rend = pillar_rend
|
||||
if self.pillar_rend:
|
||||
if saltenv not in self.opts['pillar_roots']:
|
||||
self.searchpath = []
|
||||
else:
|
||||
self.searchpath = opts['file_roots'][saltenv]
|
||||
self.searchpath = opts['pillar_roots'][saltenv]
|
||||
else:
|
||||
self.searchpath = [os.path.join(opts['cachedir'], 'files', saltenv)]
|
||||
log.debug('Jinja search path: %s', self.searchpath)
|
||||
self.cached = []
|
||||
self.pillar_rend = pillar_rend
|
||||
self._file_client = None
|
||||
# Instantiate the fileclient
|
||||
self.file_client()
|
||||
|
|
|
@ -15,7 +15,8 @@ from tests.support.runtests import RUNTIME_VARS
|
|||
import salt.utils.files
|
||||
import salt.utils.platform
|
||||
|
||||
CURL = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'win', 'repo-ng', 'curl.sls')
|
||||
REPO_DIR = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'win', 'repo-ng')
|
||||
CURL = os.path.join(REPO_DIR, 'curl.sls')
|
||||
|
||||
|
||||
@skipIf(not salt.utils.platform.is_windows(), 'windows test only')
|
||||
|
@ -33,8 +34,10 @@ class WinPKGTest(ModuleCase):
|
|||
Test add and removing a new pkg sls
|
||||
in the windows software repository
|
||||
'''
|
||||
def _check_pkg(pkgs, exists=True):
|
||||
self.run_function('pkg.refresh_db')
|
||||
def _check_pkg(pkgs, check_refresh, exists=True):
|
||||
refresh = self.run_function('pkg.refresh_db')
|
||||
self.assertEqual(check_refresh, refresh['total'],
|
||||
msg='total returned {0}. Expected return {1}'.format(refresh['total'], check_refresh))
|
||||
repo_data = self.run_function('pkg.get_repo_data', timeout=300)
|
||||
repo_cache = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', 'base', 'win', 'repo-ng')
|
||||
for pkg in pkgs:
|
||||
|
@ -51,7 +54,7 @@ class WinPKGTest(ModuleCase):
|
|||
|
||||
pkgs = ['putty', '7zip']
|
||||
# check putty and 7zip are in cache and repo query
|
||||
_check_pkg(pkgs)
|
||||
_check_pkg(pkgs, 2)
|
||||
|
||||
# now add new sls
|
||||
with salt.utils.files.fopen(CURL, 'w') as fp_:
|
||||
|
@ -74,11 +77,13 @@ class WinPKGTest(ModuleCase):
|
|||
'''))
|
||||
# now check if curl is also in cache and repo query
|
||||
pkgs.append('curl')
|
||||
_check_pkg(pkgs)
|
||||
for pkg in pkgs:
|
||||
self.assertIn(pkg + '.sls', os.listdir(REPO_DIR))
|
||||
_check_pkg(pkgs, 3)
|
||||
|
||||
# remove curl sls and check its not in cache and repo query
|
||||
os.remove(CURL)
|
||||
_check_pkg(['curl'], exists=False)
|
||||
_check_pkg(['curl'], 2, exists=False)
|
||||
|
||||
def tearDown(self):
|
||||
if os.path.isfile(CURL):
|
||||
|
|
|
@ -101,13 +101,13 @@ from salt.utils.gitfs import (
|
|||
# Check for requisite components
|
||||
try:
|
||||
HAS_GITPYTHON = GITPYTHON_VERSION >= GITPYTHON_MINVER
|
||||
except ImportError:
|
||||
except Exception:
|
||||
HAS_GITPYTHON = False
|
||||
|
||||
try:
|
||||
HAS_PYGIT2 = PYGIT2_VERSION >= PYGIT2_MINVER \
|
||||
and LIBGIT2_VERSION >= LIBGIT2_MINVER
|
||||
except AttributeError:
|
||||
except Exception:
|
||||
HAS_PYGIT2 = False
|
||||
|
||||
HAS_SSHD = bool(salt.utils.path.which('sshd'))
|
||||
|
|
|
@ -168,24 +168,3 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
|
|||
finally:
|
||||
if self.test_symlink_list_file_roots:
|
||||
self.opts['file_roots'] = orig_file_roots
|
||||
|
||||
|
||||
class RootsLimitTraversalTest(TestCase, AdaptedConfigurationTestCaseMixin):
|
||||
|
||||
def test_limit_traversal(self):
|
||||
'''
|
||||
1) Set up a deep directory structure
|
||||
2) Enable the configuration option 'fileserver_limit_traversal'
|
||||
3) Ensure that we can find SLS files in a directory so long as there is
|
||||
an SLS file in a directory above.
|
||||
4) Ensure that we cannot find an SLS file in a directory that does not
|
||||
have an SLS file in a directory above.
|
||||
|
||||
'''
|
||||
file_client_opts = self.get_temp_config('master')
|
||||
file_client_opts['fileserver_limit_traversal'] = True
|
||||
|
||||
ret = salt.fileclient.Client(file_client_opts).list_states('base')
|
||||
self.assertIn('test_deep.test', ret)
|
||||
self.assertIn('test_deep.a.test', ret)
|
||||
self.assertNotIn('test_deep.b.2.test', ret)
|
||||
|
|
|
@ -19,6 +19,8 @@ from tests.support.mock import (
|
|||
# Import Salt Libs
|
||||
from salt.exceptions import SaltInvocationError
|
||||
import salt.states.test as test
|
||||
from salt.utils.odict import OrderedDict
|
||||
from salt.ext import six
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
|
@ -310,6 +312,48 @@ class TestTestCase(TestCase, LoaderModuleMockMixin):
|
|||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertEqual(test.check_pillar('salt', present='my_pillar'), ret)
|
||||
|
||||
def test_check_pillar_string(self):
|
||||
'''
|
||||
Test to ensure the check_pillar function
|
||||
works properly with the 'key_type' checks,
|
||||
using the string key_type.
|
||||
'''
|
||||
ret = {
|
||||
'name': 'salt',
|
||||
'changes': {},
|
||||
'result': True,
|
||||
'comment': ''
|
||||
}
|
||||
pillar_return = 'I am a pillar.'
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertEqual(test.check_pillar('salt', string='my_pillar'), ret)
|
||||
# With unicode (py2) or str (py3) strings
|
||||
pillar_return = six.text_type('I am a pillar.')
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertEqual(test.check_pillar('salt', string='my_pillar'), ret)
|
||||
# With a dict
|
||||
pillar_return = {'this': 'dictionary'}
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', string='my_pillar')['result'])
|
||||
# With a list
|
||||
pillar_return = ['I am a pillar.']
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', string='my_pillar')['result'])
|
||||
# With a boolean
|
||||
pillar_return = True
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', string='my_pillar')['result'])
|
||||
# With an int
|
||||
pillar_return = 1
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', string='my_pillar')['result'])
|
||||
|
||||
def test_check_pillar_dictionary(self):
|
||||
'''
|
||||
Test to ensure the check_pillar function
|
||||
|
@ -326,3 +370,28 @@ class TestTestCase(TestCase, LoaderModuleMockMixin):
|
|||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertEqual(test.check_pillar('salt', dictionary='my_pillar'), ret)
|
||||
# With an ordered dict
|
||||
pillar_return = OrderedDict({'this': 'dictionary'})
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertEqual(test.check_pillar('salt', dictionary='my_pillar'), ret)
|
||||
# With a string
|
||||
pillar_return = 'I am a pillar.'
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result'])
|
||||
# With a list
|
||||
pillar_return = ['I am a pillar.']
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result'])
|
||||
# With a boolean
|
||||
pillar_return = True
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result'])
|
||||
# With an int
|
||||
pillar_return = 1
|
||||
pillar_mock = MagicMock(return_value=pillar_return)
|
||||
with patch.dict(test.__salt__, {'pillar.get': pillar_mock}):
|
||||
self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result'])
|
||||
|
|
|
@ -314,7 +314,7 @@ class PillarTestCase(TestCase):
|
|||
'extension_modules': '',
|
||||
}
|
||||
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='dev')
|
||||
self.assertEqual(pillar.opts['file_roots'],
|
||||
self.assertEqual(pillar.opts['pillar_roots'],
|
||||
{'base': ['/srv/pillar/base'], 'dev': ['/srv/pillar/__env__']})
|
||||
|
||||
def test_ignored_dynamic_pillarenv(self):
|
||||
|
@ -329,7 +329,7 @@ class PillarTestCase(TestCase):
|
|||
'extension_modules': '',
|
||||
}
|
||||
pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='base')
|
||||
self.assertEqual(pillar.opts['file_roots'], {'base': ['/srv/pillar/base']})
|
||||
self.assertEqual(pillar.opts['pillar_roots'], {'base': ['/srv/pillar/base']})
|
||||
|
||||
def test_malformed_pillar_sls(self):
|
||||
with patch('salt.pillar.compile_template') as compile_template:
|
||||
|
|
Loading…
Add table
Reference in a new issue