mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Merge pull request #34525 from rallytime/merge-develop
[develop] Merge forward from 2016.3 to develop
This commit is contained in:
commit
907f356770
@ -1207,8 +1207,73 @@ environments is to isolate via the top file.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
environment: None
|
||||
environment: dev
|
||||
|
||||
.. conf_minion:: top_file_merging_strategy
|
||||
|
||||
``top_file_merging_strategy``
|
||||
-----------------------------
|
||||
|
||||
Default: ``merge``
|
||||
|
||||
When no specific fileserver environment (a.k.a. ``saltenv``) has been specified
|
||||
for a :ref:`highstate <running-highstate>`, all environments' top files are
|
||||
inspected. This config option determines how the SLS targets in those top files
|
||||
are handled.
|
||||
|
||||
When set to ``merge``, the targets for all SLS files in all environments are
|
||||
merged together. A given environment's SLS targets for the highstate will
|
||||
consist of the collective SLS targets specified for that environment in all top
|
||||
files. The environments will be merged in no specific order, for greater
|
||||
control over the order in which the environments are merged use
|
||||
:conf_minion:`env_order`.
|
||||
|
||||
When set to ``same``, then for each environment, only that environment's top
|
||||
file is processed, with the others being ignored. For example, only the ``dev``
|
||||
environment's top file will be processed for the ``dev`` environment, and any
|
||||
SLS targets defined for ``dev`` in the ``base`` environment's (or any other
|
||||
environment's) top file will be ignored. If an environment does not have a top
|
||||
file, then the top file from the :conf_minion:`default_top` config parameter
|
||||
will be used as a fallback.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
top_file_merging_strategy: same
|
||||
|
||||
.. conf_minion:: env_order
|
||||
|
||||
``env_order``
|
||||
-------------
|
||||
|
||||
Default: ``[]``
|
||||
|
||||
When :conf_minion:`top_file_merging_strategy` is set to ``merge``, and no
|
||||
environment is specified for a :ref:`highstate <running-highstate>`, this
|
||||
config option allows for the order in which top files are merged to be
|
||||
explicitly defined.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
env_order:
|
||||
- base
|
||||
- dev
|
||||
- qa
|
||||
|
||||
.. conf_minion:: default_top
|
||||
|
||||
``default_top``
|
||||
---------------
|
||||
|
||||
Default: ``base``
|
||||
|
||||
When :conf_minion:`top_file_merging_strategy` is set to ``same``, and no
|
||||
environment is specified for a :ref:`highstate <running-highstate>`, this
|
||||
config option specifies a fallback environment in which to look for a top file
|
||||
if an environment lacks one.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
default_top: dev
|
||||
|
||||
File Directory Settings
|
||||
=======================
|
||||
|
@ -30,22 +30,22 @@ A Basic Example
|
||||
|
||||
Top files have three components:
|
||||
|
||||
- Environment: A state tree directory containing a set of state files to
|
||||
configure systems.
|
||||
- **Environment:** A state tree directory containing a set of state files to
|
||||
configure systems.
|
||||
|
||||
- Target: A grouping of machines which will have a set of states applied
|
||||
to them.
|
||||
- **Target:** A grouping of machines which will have a set of states applied to
|
||||
them.
|
||||
|
||||
- State files: A list of state files to apply to a target. Each state
|
||||
file describes one or more states to be configured and enforced
|
||||
on the targeted machines.
|
||||
- **State files:** A list of state files to apply to a target. Each state file
|
||||
describes one or more states to be configured and enforced on the targeted
|
||||
machines.
|
||||
|
||||
|
||||
The relationship between these three components is nested as follows:
|
||||
|
||||
- Environments contain targets
|
||||
- Environments contain targets
|
||||
|
||||
- Targets contain states
|
||||
- Targets contain states
|
||||
|
||||
|
||||
Putting these concepts together, we can describe a scenario in which all
|
||||
@ -172,26 +172,28 @@ Choosing an Environment to Target
|
||||
|
||||
The top file is used to assign a minion to an environment unless overridden
|
||||
using the methods described below. The environment in the top file must match
|
||||
an environment in :conf_master:`file_roots` in order for any states to be
|
||||
applied to that minion. The states that will be applied to a minion in a given
|
||||
environment can be viewed using the :py:func:`state.show_top
|
||||
<salt.modules.state.show_top>` execution function.
|
||||
valid fileserver environment (a.k.a. ``saltenv``) in order for any states to be
|
||||
applied to that minion. When using the default fileserver backend, environments
|
||||
are defined in :conf_master:`file_roots`.
|
||||
|
||||
Minions may be pinned to a particular environment by setting the ``environment``
|
||||
value in the minion configuration file. In doing so, a minion will only
|
||||
request files from the environment to which it is assigned.
|
||||
The states that will be applied to a minion in a given environment can be
|
||||
viewed using the :py:func:`state.show_top <salt.modules.state.show_top>`
|
||||
function.
|
||||
|
||||
The environment to use may also be dynamically selected at the time that
|
||||
a ``salt``, ``salt-call`` or ``salt-ssh`` by passing passing a flag to the
|
||||
execution module being called. This is most commonly done with
|
||||
functions in the ``state`` module by using the ``saltenv=`` argument. For
|
||||
example, to run a ``highstate`` on all minions, using the state files in
|
||||
the ``prod`` state tree, run: ``salt '*' state.highstate saltenv=prod``.
|
||||
Minions may be pinned to a particular environment by setting the
|
||||
:conf_minion:`environment` value in the minion configuration file. In doing so,
|
||||
a minion will only request files from the environment to which it is assigned.
|
||||
|
||||
The environment may also be dynamically selected at runtime by passing it to
|
||||
the ``salt``, ``salt-call`` or ``salt-ssh`` command. This is most commonly done
|
||||
with functions in the ``state`` module by using the ``saltenv`` argument. For
|
||||
example, to run a ``highstate`` on all minions, using only the top file and SLS
|
||||
files in the ``prod`` environment, run: ``salt '*' state.highstate
|
||||
saltenv=prod``.
|
||||
|
||||
.. note::
|
||||
Not all functions accept ``saltenv`` as an argument See individual
|
||||
function documentation to verify.
|
||||
|
||||
Not all functions accept ``saltenv`` as an argument, see the documentation
|
||||
for an individual function documentation to verify.
|
||||
|
||||
|
||||
Shorthand
|
||||
@ -305,66 +307,132 @@ of matches you can perform:
|
||||
How Top Files Are Compiled
|
||||
==========================
|
||||
|
||||
When a :ref:`highstate <running-highstate>` is executed and an environment is
|
||||
specified (either using the :conf_minion:`environment` config option or by
|
||||
passing the saltenv when executing the :ref:`highstate <running-highstate>`),
|
||||
then that environment's top file is the only top file used to assign states to
|
||||
minions, and only states from the specified environment will be run.
|
||||
|
||||
The remainder of this section applies to cases in which a :ref:`highstate
|
||||
<running-highstate>` is executed without an environment specified.
|
||||
|
||||
With no environment specified, the minion will look for a top file in each
|
||||
environment, and each top file will be processed to determine the SLS files to
|
||||
run on the minions. By default, the top files from each environment will be
|
||||
merged together. In configurations with many environments, such as with
|
||||
:ref:`GitFS <tutorial-gitfs>` where each branch and tag is treated as a
|
||||
distinct environment, this may cause unexpected results as SLS files from older
|
||||
tags cause defunct SLS files to be included in the highstate. In cases like
|
||||
this, it can be helpful to set :conf_minion:`top_file_merging_strategy` to
|
||||
``same`` to force each environment to use its own top file.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
top_file_merging_strategy: same
|
||||
|
||||
With :ref:`GitFS <tutorial-gitfs>`, it can also be helpful to simply manage
|
||||
each environment's top file separately, and/or manually specify the environment
|
||||
when executing the highstate to avoid any complicated merging scenarios.
|
||||
:conf_master:`gitfs_env_whitelist` and :conf_master:`gitfs_env_blacklist` can
|
||||
also be used to hide unneeded branches and tags from GitFS to reduce the number
|
||||
of top files in play.
|
||||
|
||||
When using multiple environments, it is not necessary to create a top file for
|
||||
each environment. The most common approach, and the easiest to maintain, is
|
||||
to use a single top file placed in only one environment.
|
||||
each environment. The easiest-to-maintain approach is to use a single top file
|
||||
placed in the ``base`` environment. This is often infeasible with :ref:`GitFS
|
||||
<tutorial-gitfs>` though, since branching/tagging can easily result in extra
|
||||
top files. However, when only the default (``roots``) fileserver backend is
|
||||
used, a single top file in the ``base`` environment is the most common way of
|
||||
configuring a :ref:`highstate <running-highstate>`.
|
||||
|
||||
However, some workflows do call for multiple top files. In this case, top
|
||||
files may be merged together to create ``high data`` for the state compiler
|
||||
to use as a source to compile states on a minion.
|
||||
The following minion configuration options affect how top files are compiled
|
||||
when no environment is specified:
|
||||
|
||||
For the following discussion of top file compilation, assume the following
|
||||
configuration:
|
||||
- :conf_minion:`top_file_merging_strategy`
|
||||
- :conf_minion:`env_order`
|
||||
- :conf_minion:`default_top`
|
||||
|
||||
Top File Compilation Examples
|
||||
=============================
|
||||
|
||||
``/etc/salt/master``:
|
||||
For the scenarios below, assume the following configuration:
|
||||
|
||||
**/etc/salt/master**:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
<snip>
|
||||
file_roots:
|
||||
first_env:
|
||||
- /srv/salt/first
|
||||
second_env:
|
||||
- /srv/salt/second
|
||||
base:
|
||||
- /srv/salt/base
|
||||
dev:
|
||||
- /srv/salt/dev
|
||||
qa:
|
||||
- /srv/salt/qa
|
||||
|
||||
|
||||
``/srv/salt/first/top.sls``:
|
||||
**/srv/salt/base/top.sls**:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
first_env:
|
||||
base:
|
||||
'*':
|
||||
- first
|
||||
second_env:
|
||||
- base1
|
||||
dev:
|
||||
'*':
|
||||
- second
|
||||
- dev1
|
||||
qa:
|
||||
'*':
|
||||
- qa1
|
||||
|
||||
The astute reader will ask how the state compiler resolves which should be
|
||||
an obvious conflict if a minion is not pinned to a particular environment
|
||||
and if no environment argument is passed into a state function.
|
||||
**/srv/salt/dev/top.sls**:
|
||||
|
||||
Given the above, it is initially unclear whether ``first.sls`` will be applied
|
||||
or whether ``second.sls`` will be applied in a ``salt '*' state.highstate`` command.
|
||||
.. code-block:: yaml
|
||||
|
||||
When conflicting keys arise, there are several configuration options which
|
||||
control the behaviour of salt:
|
||||
base:
|
||||
'minion1':
|
||||
- base2
|
||||
dev:
|
||||
'minion2':
|
||||
- dev2
|
||||
qa:
|
||||
'*':
|
||||
- qa2
|
||||
|
||||
- ``env_order``
|
||||
Setting ``env_order`` will set the order in which environments are processed
|
||||
by the state compiler.
|
||||
.. note::
|
||||
For the purposes of these examples, there is no top file in the ``qa``
|
||||
environment.
|
||||
|
||||
- ``top_file_merging_strategy``
|
||||
Can be set to ``same``, which will process only the top file from the environment
|
||||
that the minion belongs to via the ``environment`` configuration setting or
|
||||
the environment that is requested via the ``saltenv`` argument supported
|
||||
by some functions in the ``state`` module.
|
||||
Scenario 1 - ``dev`` Environment Specified
|
||||
------------------------------------------
|
||||
|
||||
Can also be set to ``merge``. This is the default. When set to ``merge``,
|
||||
top files will be merged together. The order in which top files are
|
||||
merged together can be controlled with ``env_order``.
|
||||
In this scenario, the :ref:`highstate <running-highstate>` was either invoked
|
||||
with ``saltenv=dev`` or the minion has ``environment: dev`` set in the minion
|
||||
config file. The result will be that only the ``dev2`` SLS from the dev
|
||||
environment will be part of the :ref:`highstate <running-highstate>`, and it
|
||||
will be applied to minion2, while minion1 will have no states applied to it.
|
||||
|
||||
- ``default_top``
|
||||
If ``top_file_merging_strategy`` is set to ``same`` and an environment does
|
||||
not contain a top file, the top file in the environment specified by
|
||||
``default_top`` will be used instead.
|
||||
If the ``base`` environment were specified, the result would be that only the
|
||||
``base1`` SLS from the ``base`` environment would be part of the
|
||||
:ref:`highstate <running-highstate>`, and it would be applied to all minions.
|
||||
|
||||
If the ``qa`` environment were specified, the :ref:`highstate
|
||||
<running-highstate>` would exit with an error.
|
||||
|
||||
Scenario 2 - No Environment Specified, :conf_minion:`top_file_merging_strategy` is "merge"
|
||||
------------------------------------------------------------------------------------------
|
||||
|
||||
In this scenario, ``base1`` from the ``base`` environment, ``dev1`` from the
|
||||
``dev`` environment, and ``qa1`` from the ``qa`` environment are applied to all
|
||||
minions. Additionally, ``base2`` from the ``base`` environment is applied to
|
||||
minion1, and ``dev2`` from the ``dev`` environment is applied to minion2.
|
||||
|
||||
Scenario 3 - No Environment Specified, :conf_minion:`top_file_merging_strategy` is "same"
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
In this scenario, ``base1`` from the ``base`` environment is applied to all
|
||||
minions. Additionally, ``dev2`` from the ``dev`` environment is applied to
|
||||
minion2.
|
||||
|
||||
If :conf_minion:`default_top` is unset (or set to ``base``, which happens to be
|
||||
the default), then ``qa1`` from the ``qa`` environment will be applied to all
|
||||
minions. If :conf_minion:`default_top` were set to ``dev``, then ``qa2`` from
|
||||
the ``qa`` environment would be applied to all minions.
|
||||
|
@ -45,6 +45,10 @@ minion configuration file:
|
||||
The beacon system, like many others in Salt, can also be configured via the
|
||||
minion pillar, grains, or local config file.
|
||||
|
||||
.. note::
|
||||
The `inotify` beacon only works on OSes that have `inotify` kernel support.
|
||||
Currently this excludes FreeBSD, Mac OS X, and Windows.
|
||||
|
||||
Beacon Monitoring Interval
|
||||
--------------------------
|
||||
|
||||
|
@ -15,6 +15,11 @@ configuring one or more repositories in :conf_master:`gitfs_remotes`.
|
||||
|
||||
Branches and tags become Salt fileserver environments.
|
||||
|
||||
.. note::
|
||||
Branching and tagging can result in a lot of potentially-conflicting
|
||||
:ref:`top files <states-top>`, for this reason it may be useful to set
|
||||
:conf_minion:`top_file_merging_strategy` to ``same`` in the minions' config
|
||||
files if the top files are being managed in a GitFS repo.
|
||||
|
||||
.. _gitfs-dependencies:
|
||||
|
||||
|
@ -164,7 +164,7 @@ Downloading the script from develop branch:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget https://bootstrap.saltstack.com/develop
|
||||
wget -O bootstrap_salt.sh https://bootstrap.saltstack.com/develop
|
||||
sudo sh bootstrap_salt.sh
|
||||
|
||||
Installing a specific version from git using ``wget``:
|
||||
|
@ -197,13 +197,13 @@ sudo -H $MAKE install
|
||||
############################################################################
|
||||
echo -n -e "\033]0;Build_Env: Python\007"
|
||||
|
||||
PKGURL="https://www.python.org/ftp/python/2.7.11/Python-2.7.11.tar.xz"
|
||||
PKGDIR="Python-2.7.11"
|
||||
PKGURL="https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tar.xz"
|
||||
PKGDIR="Python-2.7.12"
|
||||
|
||||
download $PKGURL
|
||||
|
||||
echo "################################################################################"
|
||||
echo "Building Python 2.7.11"
|
||||
echo "Building Python 2.7.12"
|
||||
echo "################################################################################"
|
||||
echo "Note there are some test failures"
|
||||
cd $PKGDIR
|
||||
|
@ -1 +0,0 @@
|
||||
72166763a2fe6aab45ecf378f55a1efc7322d1742c4638bae84f4ed4b9fb4c01f2a0293733c64426ae2c70df24d95ff2b1e2a4f3c2715de00d8f320d4d939ea0 Python-2.7.11.tar.xz
|
1
pkg/osx/shasums/Python-2.7.12.tar.xz.sha512
Normal file
1
pkg/osx/shasums/Python-2.7.12.tar.xz.sha512
Normal file
@ -0,0 +1 @@
|
||||
6ddbbce47cc49597433d98ca05c2f62f07ed1070807b645602a8e9e9b996adc6fa66fa20a33cd7d23d4e7e925e25071d7301d288149fbe4e8c5f06d5438dda1f ./Python-2.7.12.tar.xz
|
@ -45,7 +45,7 @@ Function Get-Settings {
|
||||
# Filenames for 64 bit Windows
|
||||
$64bitPrograms = @{
|
||||
"PyCrypto" = "pycrypto-2.6.1-cp27-none-win_amd64.whl"
|
||||
"Python" = "python-2.7.11.amd64.msi"
|
||||
"Python" = "python-2.7.12.amd64.msi"
|
||||
"PyYAML" = "PyYAML-3.11.win-amd64-py2.7.exe"
|
||||
}
|
||||
$ini.Add("64bitPrograms", $64bitPrograms)
|
||||
@ -53,7 +53,7 @@ Function Get-Settings {
|
||||
# Filenames for 32 bit Windows
|
||||
$32bitPrograms = @{
|
||||
"PyCrypto" = "pycrypto-2.6.1-cp27-none-win32.whl"
|
||||
"Python" = "python-2.7.11.msi"
|
||||
"Python" = "python-2.7.12.msi"
|
||||
"PyYAML" = "PyYAML-3.11.win32-py2.7.exe"
|
||||
}
|
||||
$ini.Add("32bitPrograms", $32bitPrograms)
|
||||
|
@ -846,14 +846,6 @@ def create(vm_):
|
||||
'access_ip' in node.get('extra', {}):
|
||||
result = [node['extra']['access_ip']]
|
||||
|
||||
if not result:
|
||||
temp_dd = node.get('addresses', {})
|
||||
for k, addr_ll in temp_dd.iteritems():
|
||||
for addr_dd in addr_ll:
|
||||
addr = addr_dd.get('addr', None)
|
||||
if addr is not None:
|
||||
result.append(addr.strip())
|
||||
|
||||
private = node.get('private_ips', [])
|
||||
public = node.get('public_ips', [])
|
||||
fixed = node.get('fixed_ips', [])
|
||||
|
@ -1382,7 +1382,7 @@ class LazyLoader(salt.utils.lazy.LazyDict):
|
||||
module_name,
|
||||
)
|
||||
if virtual_err is not None:
|
||||
log.debug('Error loading {0}.{1}: {2}'.format(self.tag,
|
||||
log.trace('Error loading {0}.{1}: {2}'.format(self.tag,
|
||||
module_name,
|
||||
virtual_err,
|
||||
))
|
||||
|
@ -118,12 +118,16 @@ def get_config(config_file='/etc/dnsmasq.conf'):
|
||||
'''
|
||||
Dumps all options from the config file.
|
||||
|
||||
config_file
|
||||
The location of the config file from which to obtain contents.
|
||||
Defaults to ``/etc/dnsmasq.conf``.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' dnsmasq.get_config
|
||||
salt '*' dnsmasq.get_config file=/etc/dnsmasq.conf
|
||||
salt '*' dnsmasq.get_config config_file=/etc/dnsmasq.conf
|
||||
'''
|
||||
dnsopts = _parse_dnamasq(config_file)
|
||||
if 'conf-dir' in dnsopts:
|
||||
|
@ -2521,11 +2521,11 @@ def append(path, *args, **kwargs):
|
||||
if ofile.read(len(linesep)) != linesep:
|
||||
ofile.seek(0, os.SEEK_END)
|
||||
ofile.write(linesep)
|
||||
|
||||
# Append lines in text mode
|
||||
with salt.utils.fopen(path, 'r+') as ofile:
|
||||
ofile.seek(0, os.SEEK_END)
|
||||
for line in args:
|
||||
ofile.write('{0}\n'.format(line))
|
||||
with salt.utils.fopen(path, 'a') as ofile:
|
||||
for new_line in args:
|
||||
ofile.write('{0}{1}'.format(new_line, os.linesep))
|
||||
|
||||
return 'Wrote {0} lines to "{1}"'.format(len(args), path)
|
||||
|
||||
|
@ -1372,7 +1372,10 @@ def describe(cwd, rev='HEAD', user=None, ignore_retcode=False):
|
||||
cwd = _expand_path(cwd, user)
|
||||
if not isinstance(rev, six.string_types):
|
||||
rev = str(rev)
|
||||
command = ['git', 'describe', rev]
|
||||
command = ['git', 'describe']
|
||||
if _LooseVersion(version(versioninfo=False)) >= _LooseVersion('1.5.6'):
|
||||
command.append('--always')
|
||||
command.append(rev)
|
||||
return _git_run(command,
|
||||
cwd=cwd,
|
||||
runas=user,
|
||||
|
@ -12,7 +12,7 @@ import xml.etree.ElementTree as ET
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.cloud as suc
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import SaltInvocationError, CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -75,7 +75,12 @@ def _gluster_xml(cmd):
|
||||
result = __salt__['cmd.run'](
|
||||
'gluster --xml --mode=script', stdin="{0}\n".format(cmd)
|
||||
)
|
||||
root = ET.fromstring(_gluster_output_cleanup(result))
|
||||
|
||||
try:
|
||||
root = ET.fromstring(_gluster_output_cleanup(result))
|
||||
except ET.ParseError:
|
||||
raise CommandExecutionError('\n'.join(result.splitlines()[:-1]))
|
||||
|
||||
if _gluster_ok(root):
|
||||
output = root.find('output')
|
||||
if output:
|
||||
@ -84,6 +89,7 @@ def _gluster_xml(cmd):
|
||||
log.info('Gluster call "{0}" succeeded'.format(cmd))
|
||||
else:
|
||||
log.error('Failed gluster call: {0}: {1}'.format(cmd, root.find('opErrstr').text))
|
||||
|
||||
return root
|
||||
|
||||
|
||||
|
@ -139,7 +139,15 @@ def output(data):
|
||||
data = data.pop('data')
|
||||
|
||||
for host, hostdata in six.iteritems(data):
|
||||
if not isinstance(hostdata, dict):
|
||||
# Highstate return data must be a dict, if this is not the case
|
||||
# then this value is likely a retcode.
|
||||
continue
|
||||
return _format_host(host, hostdata)[0]
|
||||
log.error(
|
||||
'Data passed to highstate outputter is not a valid highstate return: %s',
|
||||
data
|
||||
)
|
||||
|
||||
|
||||
def _format_host(host, data):
|
||||
|
@ -2576,9 +2576,14 @@ class BaseHighState(object):
|
||||
tops[saltenv].append({})
|
||||
log.debug('No contents loaded for env: {0}'.format(saltenv))
|
||||
if found > 1:
|
||||
log.warning('Top file merge strategy set to \'merge\' and multiple top files found. '
|
||||
'Top file merging order is undefined; '
|
||||
'for better results use \'same\' option')
|
||||
log.warning(
|
||||
'top_file_merging_strategy is set to \'merge\' and '
|
||||
'multiple top files were found. Merging order is not '
|
||||
'deterministic, it may be desirable to either set '
|
||||
'top_file_merging_strategy to \'same\' or use the '
|
||||
'\'env_order\' configuration parameter to specify the '
|
||||
'merging order.'
|
||||
)
|
||||
|
||||
if found == 0:
|
||||
log.error('No contents found in top file')
|
||||
|
@ -3829,17 +3829,17 @@ def append(name,
|
||||
|
||||
.. versionadded:: 0.9.5
|
||||
'''
|
||||
name = os.path.expanduser(name)
|
||||
|
||||
ret = {
|
||||
'name': name,
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'pchanges': {},
|
||||
'result': False,
|
||||
'comment': ''}
|
||||
|
||||
if not name:
|
||||
return _error(ret, 'Must provide name to file.append')
|
||||
|
||||
name = os.path.expanduser(name)
|
||||
|
||||
if sources is None:
|
||||
sources = []
|
||||
|
||||
@ -3888,17 +3888,16 @@ def append(name,
|
||||
|
||||
with salt.utils.fopen(name, 'rb') as fp_:
|
||||
slines = fp_.readlines()
|
||||
slines = [item.rstrip() for item in slines]
|
||||
|
||||
count = 0
|
||||
test_lines = []
|
||||
|
||||
append_lines = []
|
||||
try:
|
||||
for chunk in text:
|
||||
if ignore_whitespace:
|
||||
if __salt__['file.search'](
|
||||
name,
|
||||
salt.utils.build_whitespace_split_regex(chunk),
|
||||
multiline=True):
|
||||
name,
|
||||
salt.utils.build_whitespace_split_regex(chunk),
|
||||
multiline=True):
|
||||
continue
|
||||
elif __salt__['file.search'](
|
||||
name,
|
||||
@ -3906,37 +3905,39 @@ def append(name,
|
||||
multiline=True):
|
||||
continue
|
||||
|
||||
lines = chunk.splitlines()
|
||||
for line_item in chunk.splitlines():
|
||||
append_lines.append('{0}'.format(line_item))
|
||||
|
||||
for line in lines:
|
||||
if __opts__['test']:
|
||||
ret['comment'] = 'File {0} is set to be updated'.format(name)
|
||||
ret['result'] = None
|
||||
test_lines.append('{0}\n'.format(line))
|
||||
else:
|
||||
__salt__['file.append'](name, line)
|
||||
count += 1
|
||||
except TypeError:
|
||||
return _error(ret, 'No text found to append. Nothing appended')
|
||||
|
||||
if __opts__['test']:
|
||||
nlines = slines + test_lines
|
||||
ret['comment'] = 'File {0} is set to be updated'.format(name)
|
||||
ret['result'] = None
|
||||
nlines = list(slines)
|
||||
nlines.extend(append_lines)
|
||||
if slines != nlines:
|
||||
if not salt.utils.istextfile(name):
|
||||
ret['changes']['diff'] = 'Replace binary file'
|
||||
else:
|
||||
# Changes happened, add them
|
||||
ret['changes']['diff'] = (
|
||||
''.join(difflib.unified_diff(slines, nlines))
|
||||
'\n'.join(difflib.unified_diff(slines, nlines))
|
||||
)
|
||||
else:
|
||||
ret['comment'] = 'File {0} is in correct state'.format(name)
|
||||
ret['result'] = True
|
||||
return ret
|
||||
|
||||
if append_lines:
|
||||
__salt__['file.append'](name, args=append_lines)
|
||||
ret['comment'] = 'Appended {0} lines'.format(len(append_lines))
|
||||
else:
|
||||
ret['comment'] = 'File {0} is in correct state'.format(name)
|
||||
|
||||
with salt.utils.fopen(name, 'rb') as fp_:
|
||||
nlines = fp_.readlines()
|
||||
nlines = [item.rstrip(os.linesep) for item in nlines]
|
||||
|
||||
if slines != nlines:
|
||||
if not salt.utils.istextfile(name):
|
||||
@ -3944,14 +3945,10 @@ def append(name,
|
||||
else:
|
||||
# Changes happened, add them
|
||||
ret['changes']['diff'] = (
|
||||
''.join(difflib.unified_diff(slines, nlines))
|
||||
)
|
||||
'\n'.join(difflib.unified_diff(slines, nlines)))
|
||||
|
||||
if count:
|
||||
ret['comment'] = 'Appended {0} lines'.format(count)
|
||||
else:
|
||||
ret['comment'] = 'File {0} is in correct state'.format(name)
|
||||
ret['result'] = True
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -1946,7 +1946,7 @@ def detached(name,
|
||||
|
||||
local_commit_id = _get_local_rev_and_branch(target, user)[0]
|
||||
|
||||
if remote_ref_type is 'hash' and __salt__['git.describe'](ref):
|
||||
if remote_ref_type is 'hash' and __salt__['git.describe'](target, ref):
|
||||
# The ref is a hash and it exists locally so skip to checkout
|
||||
hash_exists_locally = True
|
||||
else:
|
||||
@ -2110,7 +2110,7 @@ def detached(name,
|
||||
#get refs and checkout
|
||||
checkout_commit_id = ''
|
||||
if remote_ref_type is 'hash':
|
||||
if __salt__['git.describe'](ref):
|
||||
if __salt__['git.describe'](target, ref):
|
||||
checkout_commit_id = ref
|
||||
else:
|
||||
return _fail(
|
||||
|
@ -1765,31 +1765,18 @@ def latest(
|
||||
|
||||
targets = {}
|
||||
problems = []
|
||||
cmp_func = __salt__.get('pkg.version_cmp')
|
||||
minion_os = __salt__['grains.item']('os')['os']
|
||||
|
||||
if minion_os == 'Gentoo' and watch_flags:
|
||||
for pkg in desired_pkgs:
|
||||
if not avail[pkg] and not cur[pkg]:
|
||||
for pkg in desired_pkgs:
|
||||
if not avail[pkg]:
|
||||
if not cur[pkg]:
|
||||
msg = 'No information found for \'{0}\'.'.format(pkg)
|
||||
log.error(msg)
|
||||
problems.append(msg)
|
||||
else:
|
||||
if salt.utils.compare_versions(ver1=cur[pkg], oper='!=', ver2=avail[pkg], cmp_func=cmp_func):
|
||||
targets[pkg] = avail[pkg]
|
||||
else:
|
||||
if not cur[pkg] or __salt__['portage_config.is_changed_uses'](pkg):
|
||||
targets[pkg] = avail[pkg]
|
||||
else:
|
||||
for pkg in desired_pkgs:
|
||||
if pkg not in avail:
|
||||
if not cur.get(pkg):
|
||||
msg = 'No information found for \'{0}\'.'.format(pkg)
|
||||
log.error(msg)
|
||||
problems.append(msg)
|
||||
elif not cur.get(pkg) \
|
||||
or salt.utils.compare_versions(ver1=cur[pkg], oper='<', ver2=avail[pkg], cmp_func=cmp_func):
|
||||
elif watch_flags \
|
||||
and __grains__.get('os') == 'Gentoo' \
|
||||
and __salt__['portage_config.is_changed_uses'](pkg):
|
||||
targets[pkg] = avail[pkg]
|
||||
else:
|
||||
targets[pkg] = avail[pkg]
|
||||
|
||||
if problems:
|
||||
return {
|
||||
|
@ -111,9 +111,14 @@ class TestModulesGrains(integration.ModuleCase):
|
||||
test to ensure some core grains are returned
|
||||
'''
|
||||
grains = ['os', 'os_family', 'osmajorrelease', 'osrelease', 'osfullname', 'id']
|
||||
os = self.run_function('grains.get', ['os'])
|
||||
|
||||
for grain in grains:
|
||||
get_grain = self.run_function('grains.get', [grain])
|
||||
self.assertTrue(get_grain, grain + "is not available")
|
||||
if os == 'Arch' and grain in ['osmajorrelease', 'osrelease']:
|
||||
self.assertEqual(get_grain, '')
|
||||
continue
|
||||
self.assertTrue(get_grain)
|
||||
|
||||
|
||||
class GrainsAppendTestCase(integration.ModuleCase):
|
||||
|
@ -10,29 +10,23 @@ from __future__ import absolute_import
|
||||
|
||||
# Salt libs
|
||||
import integration
|
||||
import salt.utils
|
||||
|
||||
# Salttesting libs
|
||||
from salttesting import skipIf
|
||||
from salttesting.helpers import destructiveTest, ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
|
||||
def _check_arch_linux():
|
||||
with salt.utils.fopen('/etc/os-release', 'r') as f:
|
||||
release = f.readline()
|
||||
r = release.split('=')[1].strip().strip('"')
|
||||
return r
|
||||
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(_check_arch_linux() == 'Arch Linux', 'Network state not supported on Arch')
|
||||
class NetworkTest(integration.ModuleCase, integration.SaltReturnAssertsMixIn):
|
||||
'''
|
||||
Validate network state module
|
||||
'''
|
||||
def setUp(self):
|
||||
os_family = self.run_function('grains.get', ['os_family'])
|
||||
if os_family not in ('RedHat', 'Debian'):
|
||||
self.skipTest('Network state only supported on RedHat and Debian based systems')
|
||||
|
||||
self.run_function('cmd.run', ['ip link add name dummy0 type dummy'])
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -1088,88 +1088,6 @@ class FileTestCase(TestCase):
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(filestate.uncomment(name, regex), ret)
|
||||
|
||||
# 'append' function tests: 1
|
||||
|
||||
def test_append(self):
|
||||
'''
|
||||
Test to ensure that some text appears at the end of a file.
|
||||
'''
|
||||
name = '/etc/motd'
|
||||
source = ['salt://motd/hr-messages.tmpl']
|
||||
sources = ['salt://motd/devops-messages.tmpl']
|
||||
text = ['Trust no one unless you have eaten much salt with him.']
|
||||
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'comment': '',
|
||||
'pchanges': {},
|
||||
'changes': {}}
|
||||
|
||||
comt = ('Must provide name to file.append')
|
||||
ret.update({'comment': comt, 'name': ''})
|
||||
self.assertDictEqual(filestate.append(''), ret)
|
||||
|
||||
comt = ('source and sources are mutually exclusive')
|
||||
ret.update({'comment': comt, 'name': name})
|
||||
self.assertDictEqual(filestate.append(name, source=source,
|
||||
sources=sources), ret)
|
||||
|
||||
mock_t = MagicMock(return_value=True)
|
||||
mock_f = MagicMock(return_value=False)
|
||||
mock_err = MagicMock(side_effect=[TypeError, True, True])
|
||||
with patch.dict(filestate.__salt__,
|
||||
{'file.directory_exists': mock_f,
|
||||
'file.makedirs': mock_t,
|
||||
'file.stats': mock_f,
|
||||
'cp.get_template': mock_f,
|
||||
'file.contains_regex_multiline': mock_err,
|
||||
'file.search': mock_err}):
|
||||
with patch.object(os.path, 'isdir', mock_t):
|
||||
comt = ('The following files will be changed:\n/etc:'
|
||||
' directory - new\n')
|
||||
ret.update({'comment': comt, 'name': name, 'pchanges': {'/etc': {'directory': 'new'}}})
|
||||
self.assertDictEqual(filestate.append(name, makedirs=True), ret)
|
||||
|
||||
with patch.object(os.path, 'isabs', mock_f):
|
||||
comt = ('Specified file {0} is not an absolute path'
|
||||
.format(name))
|
||||
ret.update({'comment': comt, 'pchanges': {}})
|
||||
self.assertDictEqual(filestate.append(name), ret)
|
||||
|
||||
with patch.object(os.path, 'isabs', mock_t):
|
||||
with patch.object(os.path, 'exists', mock_t):
|
||||
comt = ("Failed to load template file {0}".format(source))
|
||||
ret.pop('pchanges')
|
||||
ret.update({'comment': comt, 'name': source, 'data': [], })
|
||||
self.assertDictEqual(filestate.append(name, source=source),
|
||||
ret)
|
||||
|
||||
ret.pop('data', None)
|
||||
ret.update({'name': name})
|
||||
with patch.object(salt.utils, 'fopen',
|
||||
MagicMock(mock_open(read_data=''))):
|
||||
comt = ('No text found to append. Nothing appended')
|
||||
ret.update({'comment': comt, 'pchanges': {}})
|
||||
self.assertDictEqual(filestate.append(name, text=text),
|
||||
ret)
|
||||
|
||||
with patch.object(salt.utils, 'istextfile', mock_f):
|
||||
with patch.dict(filestate.__opts__, {'test': True}):
|
||||
change = {'diff': 'Replace binary file'}
|
||||
ret.update({'comment': '', 'result': None,
|
||||
'changes': change})
|
||||
self.assertDictEqual(filestate.append
|
||||
(name, text=text), ret)
|
||||
|
||||
with patch.dict(filestate.__opts__,
|
||||
{'test': False}):
|
||||
comt = ('File {0} is in correct state'
|
||||
.format(name))
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': {}})
|
||||
self.assertDictEqual(filestate.append
|
||||
(name, text=text), ret)
|
||||
|
||||
# 'prepend' function tests: 1
|
||||
|
||||
def test_prepend(self):
|
||||
|
Loading…
Reference in New Issue
Block a user