Consolidate some state requisites (#55974)

* add cmd state features to state.py
also dry git state

* dry docker_container

* update docker test

* correct integration test

* clarity improvement

* lint and test fixes

* correct test

* global req updates

* doc update

* doc updates

* remove unused mac requisite implementation

* other macpackage cleanup

* handle missing user in runas

* add test cases unless/onlyif exception handling

* fix macpackage tests

* fix typo
This commit is contained in:
Christian McHugh 2020-05-07 01:06:03 +01:00 committed by GitHub
parent 7d90120182
commit 2520ae8675
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 472 additions and 810 deletions

View file

@ -992,6 +992,38 @@ if the gluster commands return a 0 ret value.
- /etc/crontab
- 'entry1'
.. _creates-requisite:
Creates
-------
.. versionadded:: Sodium
The ``creates`` requisite specifies that a state should only run when any of
the specified files do not already exist. Like ``unless``, ``creates`` requisite
operates as NAND and is useful in giving more granular control over when a state
should execute. This was previously used by the :mod:`cmd <salt.states.cmd>` and
:mod:`docker_container <salt.states.docker_container>` states.
.. code-block:: yaml
contrived creates example:
file.touch:
- name: /path/to/file
- creates: /path/to/file
``creates`` also accepts a list of files, in which case this state will
run if **any** of the files do not exist:
.. code-block:: yaml
creates list:
file.cmd:
- name: /path/to/command
- creates:
- /path/file
- /path/file2
listen
~~~~~~

View file

@ -17,6 +17,16 @@ The old syntax for the mine_function - as a dict, or as a list with dicts that
contain more than exactly one key - is still supported but discouraged in favor
of the more uniform syntax of module.run.
State updates
=============
The ``creates`` state requisite has been migrated from the
:mod:`docker_container <salt.states.docker_container>` and :mod:`cmd <salt.states.cmd>`
states to become a global option. This acts similar to an equivalent
``unless: test -f filename`` but can also accept a list of filenames. This allows
all states to take advantage of the enhanced functionality released in Neon, of allowing
salt execution modules for requisite checks.
State Execution Module
======================

View file

@ -349,7 +349,7 @@ def orchestrate(
.. seealso:: More Orchestrate documentation
* :ref:`Full Orchestrate Tutorial <orchestrate-runner>`
* :py:mod:`Docs for the ``salt`` state module <salt.states.saltmod>`
* Docs for the salt state module :py:mod:`salt.states.saltmod`
CLI Examples:

View file

@ -51,7 +51,7 @@ import salt.utils.url
# Explicit late import to avoid circular import. DO NOT MOVE THIS.
import salt.utils.yamlloader as yamlloader
from salt.exceptions import SaltRenderError, SaltReqTimeoutError
from salt.exceptions import CommandExecutionError, SaltRenderError, SaltReqTimeoutError
# Import third party libs
# pylint: disable=import-error,no-name-in-module,redefined-builtin
@ -97,6 +97,7 @@ STATE_RUNTIME_KEYWORDS = frozenset(
"failhard",
"onlyif",
"unless",
"creates",
"retry",
"order",
"parallel",
@ -900,6 +901,14 @@ class State(object):
# If either result is True, the returned result should be True
ret["skip_watch"] = _ret["skip_watch"] or ret["skip_watch"]
if "creates" in low_data:
_ret = self._run_check_creates(low_data)
ret["result"] = _ret["result"] or ret["result"]
ret["comment"].append(_ret["comment"])
if "skip_watch" in _ret:
# If either result is True, the returned result should be True
ret["skip_watch"] = _ret["skip_watch"] or ret["skip_watch"]
return ret
def _run_check_function(self, entry):
@ -935,9 +944,13 @@ class State(object):
for entry in low_data_onlyif:
if isinstance(entry, six.string_types):
cmd = self.functions["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_opts
)
try:
cmd = self.functions["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_opts
)
except CommandExecutionError:
# Command failed, notify onlyif to skip running the item
cmd = 100
log.debug("Last command return code: %s", cmd)
_check_cmd(cmd)
elif isinstance(entry, dict):
@ -994,10 +1007,14 @@ class State(object):
for entry in low_data_unless:
if isinstance(entry, six.string_types):
cmd = self.functions["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_opts
)
log.debug("Last command return code: %s", cmd)
try:
cmd = self.functions["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_opts
)
log.debug("Last command return code: %s", cmd)
except CommandExecutionError:
# Command failed, so notify unless to skip the item
cmd = 0
_check_cmd(cmd)
elif isinstance(entry, dict):
if "fun" not in entry:
@ -1061,6 +1078,30 @@ class State(object):
return ret
return ret
def _run_check_creates(self, low_data):
"""
Check that listed files exist
"""
ret = {"result": False}
if isinstance(low_data["creates"], six.string_types) and os.path.exists(
low_data["creates"]
):
ret["comment"] = "{0} exists".format(low_data["creates"])
ret["result"] = True
ret["skip_watch"] = True
elif isinstance(low_data["creates"], list) and all(
[os.path.exists(path) for path in low_data["creates"]]
):
ret["comment"] = "All files in creates exist"
ret["result"] = True
ret["skip_watch"] = True
else:
ret["comment"] = "Creates files not found"
ret["result"] = False
return ret
def reset_run_num(self):
"""
Rest the run_num value to 0
@ -2068,11 +2109,9 @@ class State(object):
# that's not found in cdata, we look for what we're being passed in
# the original data, namely, the special dunder __env__. If that's
# not found we default to 'base'
req_list = ("unless", "onlyif", "creates")
if (
"unless" in low
and "{0[state]}.mod_run_check".format(low) not in self.states
) or (
"onlyif" in low
any(req in low for req in req_list)
and "{0[state]}.mod_run_check".format(low) not in self.states
):
ret.update(self._run_check(low))

View file

@ -46,7 +46,8 @@ run if **any** of the files do not exist:
.. note::
The ``creates`` option was added to version 2014.7.0
The ``creates`` option was added to the cmd state in version 2014.7.0,
and made a global requisite in Sodium.
Sometimes when running a command that starts up a daemon, the init script
doesn't return properly which causes Salt to wait indefinitely for a response.
@ -323,103 +324,8 @@ def _is_true(val):
raise ValueError("Failed parsing boolean value: {0}".format(val))
def mod_run_check(cmd_kwargs, onlyif, unless, creates):
"""
Execute the onlyif and unless logic.
Return a result dict if:
* onlyif failed (onlyif != 0)
* unless succeeded (unless == 0)
else return True
"""
# never use VT for onlyif/unless executions because this will lead
# to quote problems
cmd_kwargs = copy.deepcopy(cmd_kwargs)
cmd_kwargs["use_vt"] = False
cmd_kwargs["bg"] = False
if onlyif is not None:
if isinstance(onlyif, six.string_types):
cmd = __salt__["cmd.retcode"](
onlyif, ignore_retcode=True, python_shell=True, **cmd_kwargs
)
log.debug("Last command return code: {0}".format(cmd))
if cmd != 0:
return {
"comment": "onlyif condition is false",
"skip_watch": True,
"result": True,
}
elif isinstance(onlyif, list):
for entry in onlyif:
cmd = __salt__["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_kwargs
)
log.debug("Last command '{0}' return code: {1}".format(entry, cmd))
if cmd != 0:
return {
"comment": "onlyif condition is false: {0}".format(entry),
"skip_watch": True,
"result": True,
}
elif not isinstance(onlyif, six.string_types):
if not onlyif:
log.debug("Command not run: onlyif did not evaluate to string_type")
return {
"comment": "onlyif condition is false",
"skip_watch": True,
"result": True,
}
if unless is not None:
if isinstance(unless, six.string_types):
cmd = __salt__["cmd.retcode"](
unless, ignore_retcode=True, python_shell=True, **cmd_kwargs
)
log.debug("Last command return code: {0}".format(cmd))
if cmd == 0:
return {
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
}
elif isinstance(unless, list):
cmd = []
for entry in unless:
cmd.append(
__salt__["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True, **cmd_kwargs
)
)
log.debug("Last command return code: {0}".format(cmd))
if all([c == 0 for c in cmd]):
return {
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
}
elif not isinstance(unless, six.string_types):
if unless:
log.debug("Command not run: unless did not evaluate to string_type")
return {
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
}
if isinstance(creates, six.string_types) and os.path.exists(creates):
return {"comment": "{0} exists".format(creates), "result": True}
elif isinstance(creates, list) and all([os.path.exists(path) for path in creates]):
return {"comment": "All files in creates exist", "result": True}
# No reason to stop, return True
return True
def wait(
name,
onlyif=None,
unless=None,
creates=None,
cwd=None,
root=None,
runas=None,
@ -445,14 +351,6 @@ def wait(
The command to execute, remember that the command will execute with the
path and permissions of the salt-minion.
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns true
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns false
cwd
The current working directory to execute the command in, defaults to
/root
@ -562,8 +460,6 @@ def wait_script(
name,
source=None,
template=None,
onlyif=None,
unless=None,
cwd=None,
runas=None,
shell=None,
@ -594,14 +490,6 @@ def wait_script(
The command to execute, remember that the command will execute with the
path and permissions of the salt-minion.
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns true
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns false
cwd
The current working directory to execute the command in, defaults to
/root
@ -694,9 +582,6 @@ def wait_script(
def run(
name,
onlyif=None,
unless=None,
creates=None,
cwd=None,
root=None,
runas=None,
@ -721,14 +606,6 @@ def run(
The command to execute, remember that the command will execute with the
path and permissions of the salt-minion.
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns a zero exit status
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns a non-zero exit status
cwd
The current working directory to execute the command in, defaults to
/root
@ -906,11 +783,6 @@ def run(
}
)
cret = mod_run_check(cmd_kwargs, onlyif, unless, creates)
if isinstance(cret, dict):
ret.update(cret)
return ret
if __opts__["test"] and not test_name:
ret["result"] = None
ret["comment"] = 'Command "{0}" would have been executed'.format(name)
@ -956,9 +828,6 @@ def script(
name,
source=None,
template=None,
onlyif=None,
unless=None,
creates=None,
cwd=None,
runas=None,
shell=None,
@ -991,14 +860,6 @@ def script(
Either "cmd arg1 arg2 arg3..." (cmd is not used) or a source
"salt://...".
onlyif
Run the named command only if the command passed to the ``onlyif``
option returns true
unless
Run the named command only if the command passed to the ``unless``
option returns false
cwd
The current working directory to execute the command in, defaults to
/root
@ -1153,8 +1014,6 @@ def script(
"runas": runas,
"shell": shell or __grains__["shell"],
"env": env,
"onlyif": onlyif,
"unless": unless,
"cwd": cwd,
"template": template,
"umask": umask,
@ -1182,11 +1041,6 @@ def script(
if not cmd_kwargs.get("args", None) and len(name.split()) > 1:
cmd_kwargs.update({"args": name.split(" ", 1)[1]})
cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless, creates)
if isinstance(cret, dict):
ret.update(cret)
return ret
if __opts__["test"] and not test_name:
ret["result"] = None
ret["comment"] = "Command '{0}' would have been " "executed".format(name)
@ -1228,9 +1082,6 @@ def call(
func,
args=(),
kws=None,
onlyif=None,
unless=None,
creates=None,
output_loglevel="debug",
hide_output=False,
use_vt=False,
@ -1241,13 +1092,6 @@ def call(
declaration. This function is mainly used by the
:mod:`salt.renderers.pydsl` renderer.
The interpretation of ``onlyif`` and ``unless`` arguments are identical to
those of :mod:`cmd.run <salt.states.cmd.run>`, and all other
arguments(``cwd``, ``runas``, ...) allowed by :mod:`cmd.run
<salt.states.cmd.run>` are allowed here, except that their effects apply
only to the commands specified in `onlyif` and `unless` rather than to the
function to be invoked.
In addition, the ``stateful`` argument has no effects here.
The return value of the invoked function will be interpreted as follows.
@ -1281,11 +1125,6 @@ def call(
"umask": kwargs.get("umask"),
}
cret = mod_run_check(cmd_kwargs, onlyif, unless, creates)
if isinstance(cret, dict):
ret.update(cret)
return ret
if not kws:
kws = {}
result = func(*args, **kws)
@ -1306,9 +1145,6 @@ def wait_call(
func,
args=(),
kws=None,
onlyif=None,
unless=None,
creates=None,
stateful=False,
use_vt=False,
output_loglevel="debug",

View file

@ -49,7 +49,6 @@ from __future__ import absolute_import, print_function, unicode_literals
import copy
import logging
import os
import salt.utils.args
import salt.utils.data
@ -2093,9 +2092,6 @@ def running(
def run(
name,
image=None,
onlyif=None,
unless=None,
creates=None,
bg=False,
failhard=True,
replace=False,
@ -2131,15 +2127,6 @@ def run(
Additionally, the following arguments are supported:
onlyif
A command or list of commands to run as a check. The container will
only run if any of the specified commands returns a zero exit status.
unless
A command or list of commands to run as a check. The container will
only run if any of the specified commands returns a non-zero exit
status.
creates
A path or list of paths. Only run if one or more of the specified paths
do not exist on the minion.
@ -2213,11 +2200,6 @@ def run(
elif not isinstance(image, six.string_types):
image = six.text_type(image)
cret = mod_run_check(onlyif, unless, creates)
if isinstance(cret, dict):
ret.update(cret)
return ret
try:
if "networks" in kwargs and kwargs["networks"] is not None:
kwargs["networks"] = _parse_networks(kwargs["networks"])
@ -2230,11 +2212,6 @@ def run(
ret["comment"] = exc.__str__()
return ret
cret = mod_run_check(onlyif, unless, creates)
if isinstance(cret, dict):
ret.update(cret)
return ret
if __opts__["test"]:
ret["result"] = None
ret["comment"] = "Container would be run{0}".format(
@ -2532,82 +2509,6 @@ def absent(name, force=False):
return ret
def mod_run_check(onlyif, unless, creates):
"""
Execute the onlyif/unless/creates logic. Returns a result dict if any of
the checks fail, otherwise returns True
"""
cmd_kwargs = {"use_vt": False, "bg": False}
if onlyif is not None:
if isinstance(onlyif, six.string_types):
onlyif = [onlyif]
if not isinstance(onlyif, list) or not all(
isinstance(x, six.string_types) for x in onlyif
):
return {
"comment": "onlyif is not a string or list of strings",
"skip_watch": True,
"result": True,
}
for entry in onlyif:
retcode = __salt__["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True
)
if retcode != 0:
return {
"comment": "onlyif command {0} returned exit code of {1}".format(
entry, retcode
),
"skip_watch": True,
"result": True,
}
if unless is not None:
if isinstance(unless, six.string_types):
unless = [unless]
if not isinstance(unless, list) or not all(
isinstance(x, six.string_types) for x in unless
):
return {
"comment": "unless is not a string or list of strings",
"skip_watch": True,
"result": True,
}
for entry in unless:
retcode = __salt__["cmd.retcode"](
entry, ignore_retcode=True, python_shell=True
)
if retcode == 0:
return {
"comment": "unless command {0} returned exit code of {1}".format(
entry, retcode
),
"skip_watch": True,
"result": True,
}
if creates is not None:
if isinstance(creates, six.string_types):
creates = [creates]
if not isinstance(creates, list) or not all(
isinstance(x, six.string_types) for x in creates
):
return {
"comment": "creates is not a string or list of strings",
"skip_watch": True,
"result": True,
}
if all(os.path.exists(x) for x in creates):
return {
"comment": "All specified paths in 'creates' " "argument exist",
"result": True,
}
# No reason to stop, return True
return True
def mod_watch(name, sfun=None, **kwargs):
"""
The docker_container watcher, called to invoke the watch command.

View file

@ -13,7 +13,6 @@ States to manage git repositories and git configuration
from __future__ import absolute_import, print_function, unicode_literals
# Import python libs
import copy
import errno
import logging
import os
@ -281,8 +280,6 @@ def latest(
identity=None,
https_user=None,
https_pass=None,
onlyif=None,
unless=None,
refspec_branch="*",
refspec_tag="*",
output_encoding=None,
@ -543,14 +540,6 @@ def latest(
.. versionadded:: 2015.5.0
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns true
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns false
refspec_branch : *
A glob expression defining which branches to retrieve when fetching.
See `git-fetch(1)`_ for more information on how refspecs work.
@ -724,12 +713,6 @@ def latest(
if "shell" in __grains__:
run_check_cmd_kwargs["shell"] = __grains__["shell"]
# check if git.latest should be applied
cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless)
if isinstance(cret, dict):
ret.update(cret)
return ret
refspecs = (
[
"refs/heads/{0}:refs/remotes/{1}/{0}".format(refspec_branch, remote),
@ -2207,8 +2190,6 @@ def detached(
identity=None,
https_user=None,
https_pass=None,
onlyif=None,
unless=None,
output_encoding=None,
**kwargs
):
@ -2281,14 +2262,6 @@ def detached(
https_pass
HTTP Basic Auth password for HTTPS (only) clones
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns true
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns false
output_encoding
Use this option to specify which encoding to use to decode the output
from any git commands which are run. This should not be needed in most
@ -2369,15 +2342,6 @@ def detached(
redacted_fetch_url = salt.utils.url.redact_http_basic_auth(desired_fetch_url)
# Check if onlyif or unless conditions match
run_check_cmd_kwargs = {"runas": user}
if "shell" in __grains__:
run_check_cmd_kwargs["shell"] = __grains__["shell"]
cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless)
if isinstance(cret, dict):
ret.update(cret)
return ret
# Determine if supplied ref is a hash
remote_rev_type = "ref"
if len(rev) <= 40 and all(x in string.hexdigits for x in rev):
@ -3337,76 +3301,3 @@ def config_set(
value_comment,
)
return ret
def mod_run_check(cmd_kwargs, onlyif, unless):
"""
Execute the onlyif and unless logic. Return a result dict if:
* onlyif failed (onlyif != 0)
* unless succeeded (unless == 0)
Otherwise, returns ``True``
"""
cmd_kwargs = copy.deepcopy(cmd_kwargs)
cmd_kwargs.update(
{"use_vt": False, "bg": False, "ignore_retcode": True, "python_shell": True}
)
if onlyif is not None:
if not isinstance(onlyif, list):
onlyif = [onlyif]
for command in onlyif:
if not isinstance(command, six.string_types) and command:
# Boolean or some other non-string which resolves to True
continue
try:
if __salt__["cmd.retcode"](command, **cmd_kwargs) == 0:
# Command exited with a zero retcode
continue
except Exception as exc: # pylint: disable=broad-except
log.exception(
"The following onlyif command raised an error: %s", command
)
return {
"comment": "onlyif raised error ({0}), see log for "
"more details".format(exc),
"result": False,
}
return {
"comment": "onlyif condition is false",
"skip_watch": True,
"result": True,
}
if unless is not None:
if not isinstance(unless, list):
unless = [unless]
for command in unless:
if not isinstance(command, six.string_types) and not command:
# Boolean or some other non-string which resolves to False
break
try:
if __salt__["cmd.retcode"](command, **cmd_kwargs) != 0:
# Command exited with a non-zero retcode
break
except Exception as exc: # pylint: disable=broad-except
log.exception(
"The following unless command raised an error: %s", command
)
return {
"comment": "unless raised error ({0}), see log for "
"more details".format(exc),
"result": False,
}
else:
return {
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
}
return True

View file

@ -54,9 +54,6 @@ def installed(
store=False,
app=False,
mpkg=False,
user=None,
onlyif=None,
unless=None,
force=False,
allow_untrusted=False,
version_check=None,
@ -84,17 +81,6 @@ def installed(
mpkg
Is the file a .mpkg? If so then we'll check all of the .pkg files found are installed
user
Name of the user performing the unless or onlyif checks
onlyif
A command to run as a check, run the named command only if the command
passed to the ``onlyif`` option returns true
unless
A command to run as a check, only run the named command if the command
passed to the ``unless`` option returns false
force
Force the package to be installed even if its already been found installed
@ -115,17 +101,6 @@ def installed(
real_pkg = name
# Check onlyif, unless first
run_check_cmd_kwargs = {"runas": user, "python_shell": True}
if "shell" in __grains__:
run_check_cmd_kwargs["shell"] = __grains__["shell"]
cret = _mod_run_check(run_check_cmd_kwargs, onlyif, unless)
if isinstance(cret, dict):
ret.update(cret)
return ret
# Check version info
if version_check is not None:
split = version_check.split("=")
@ -185,7 +160,7 @@ def installed(
pkg_ids = [os.path.basename(name)]
mount_point = os.path.dirname(name)
if onlyif is None and unless is None and version_check is None:
if version_check is None:
for p in pkg_ids:
if target[-4:] == ".app":
install_dir = target
@ -262,31 +237,3 @@ def installed(
__salt__["macpackage.unmount"](mount_point)
return ret
def _mod_run_check(cmd_kwargs, onlyif, unless):
"""
Execute the onlyif and unless logic.
Return a result dict if:
* onlyif failed (onlyif != 0)
* unless succeeded (unless == 0)
else return True
"""
if onlyif:
if __salt__["cmd.retcode"](onlyif, **cmd_kwargs) != 0:
return {
"comment": "onlyif condition is false",
"skip_watch": True,
"result": True,
}
if unless:
if __salt__["cmd.retcode"](unless, **cmd_kwargs) == 0:
return {
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
}
# No reason to stop, return True
return True

View file

@ -1584,10 +1584,20 @@ def installed(
.. seealso:: unless and onlyif
You can use the :ref:`unless <unless-requisite>` or
:ref:`onlyif <onlyif-requisite>` syntax to skip a full package run.
This can be helpful in large environments with multiple states that
include requisites for packages to be installed.
If running pkg commands together with :ref:`aggregate <mod-aggregate-state>`
isn't an option, you can use the :ref:`creates <creates-requisite>`,
:ref:`unless <unless-requisite>`, or :ref:`onlyif <onlyif-requisite>`
syntax to skip a full package run. This can be helpful in large environments
with multiple states that include requisites for packages to be installed.
.. code-block:: yaml
# Using creates for a simple single-factor check
install_nginx:
pkg.installed:
- name: nginx
- creates:
- /etc/nginx/nginx.conf
.. code-block:: yaml
@ -1600,6 +1610,12 @@ def installed(
args:
- /etc/nginx/nginx.conf
# Using unless with a shell test
install_nginx:
pkg.installed:
- name: nginx
- unless: test -f /etc/nginx/nginx.conf
.. code-block:: yaml
# Using file.search for a two-factor check
@ -1612,11 +1628,11 @@ def installed(
- /etc/nginx/nginx.conf
- 'user www-data;'
The above examples use two different methods to reasonably ensure
The above examples use different methods to reasonably ensure
that a package has already been installed. First, with checking for a
file that would be created with the package. Second, by checking for
specific text within a file that would be created or managed by salt.
With these requisists satisfied, unless will return ``True`` and the
With these requisists satisfied, creates/unless will return ``True`` and the
``pkg.installed`` state will be skipped.
.. code-block:: bash

View file

@ -891,11 +891,7 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin):
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
self.assertFalse(ret["changes"])
self.assertTrue(
ret["comment"].startswith(
"onlyif command /bin/false returned exit code of"
)
)
self.assertTrue(ret["comment"].startswith("onlyif condition is false"))
self.run_function("docker.rm", [name], force=True)
for cmd in ("/bin/true", ["/bin/true", "ls /"]):
@ -936,9 +932,7 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin):
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
self.assertFalse(ret["changes"])
self.assertEqual(
ret["comment"], "unless command /bin/true returned exit code of 0"
)
self.assertEqual(ret["comment"], "unless condition is true")
self.run_function("docker.rm", [name], force=True)
for cmd in ("/bin/false", ["/bin/false", "ls /paththatdoesnotexist"]):
@ -982,22 +976,34 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin):
good_file1 = _mkstemp()
good_file2 = _mkstemp()
for path in (good_file1, [good_file1, good_file2]):
log.debug("Trying %s", path)
ret = self.run_state(
"docker_container.run",
name=name,
image=self.image,
command="whoami",
creates=path,
)
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
self.assertFalse(ret["changes"])
self.assertEqual(
ret["comment"], "All specified paths in 'creates' argument exist"
)
self.run_function("docker.rm", [name], force=True)
log.debug("Trying %s", good_file1)
ret = self.run_state(
"docker_container.run",
name=name,
image=self.image,
command="whoami",
creates=good_file1,
)
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
self.assertFalse(ret["changes"])
self.assertEqual(ret["comment"], "{0} exists".format(good_file1))
self.run_function("docker.rm", [name], force=True)
path = [good_file1, good_file2]
log.debug("Trying %s", path)
ret = self.run_state(
"docker_container.run",
name=name,
image=self.image,
command="whoami",
creates=path,
)
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
self.assertFalse(ret["changes"])
self.assertEqual(ret["comment"], "All files in creates exist")
self.run_function("docker.rm", [name], force=True)
for path in (bad_file, [good_file1, bad_file]):
log.debug("Trying %s", path)

View file

@ -5,8 +5,6 @@
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import os.path
# Import Salt Libs
import salt.states.cmd as cmd
from salt.exceptions import CommandExecutionError
@ -25,73 +23,6 @@ class CmdTestCase(TestCase, LoaderModuleMockMixin):
def setup_loader_modules(self):
return {cmd: {"__env__": "base"}}
# 'mod_run_check' function tests: 1
def test_mod_run_check(self):
"""
Test to execute the onlyif and unless logic.
"""
cmd_kwargs = {}
creates = "/tmp"
mock = MagicMock(return_value=1)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
with patch.dict(cmd.__opts__, {"test": True}):
ret = {
"comment": "onlyif condition is false",
"result": True,
"skip_watch": True,
}
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, "", "", creates), ret
)
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, {}, "", creates), ret
)
mock = MagicMock(return_value=1)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
with patch.dict(cmd.__opts__, {"test": True}):
ret = {
"comment": "onlyif condition is false: ",
"result": True,
"skip_watch": True,
}
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, [""], "", creates), ret
)
mock = MagicMock(return_value=0)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
ret = {
"comment": "unless condition is true",
"result": True,
"skip_watch": True,
}
self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, "", creates), ret)
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, None, [""], creates), ret
)
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, None, True, creates), ret
)
with patch.object(os.path, "exists", MagicMock(sid_effect=[True, True, False])):
ret = {"comment": "/tmp exists", "result": True}
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, None, None, creates), ret
)
ret = {"comment": "All files in creates exist", "result": True}
self.assertDictEqual(
cmd.mod_run_check(cmd_kwargs, None, None, [creates]), ret
)
self.assertTrue(cmd.mod_run_check(cmd_kwargs, None, None, {}))
# 'wait' function tests: 1
def test_wait(self):
@ -153,13 +84,6 @@ class CmdTestCase(TestCase, LoaderModuleMockMixin):
ret.update({"comment": comt, "result": None, "changes": {}})
self.assertDictEqual(cmd.run(name), ret)
mock = MagicMock(return_value=1)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
with patch.dict(cmd.__opts__, {"test": False}):
comt = "onlyif condition is false"
ret.update({"comment": comt, "result": True, "skip_watch": True})
self.assertDictEqual(cmd.run(name, onlyif=""), ret)
def test_run_root(self):
"""
Test to run a command with a different root
@ -220,20 +144,6 @@ class CmdTestCase(TestCase, LoaderModuleMockMixin):
)
self.assertDictEqual(cmd.script(name), ret)
mock = MagicMock(return_value=1)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
with patch.dict(cmd.__opts__, {"test": False}):
comt = "onlyif condition is false"
ret.update(
{
"comment": comt,
"result": True,
"skip_watch": True,
"changes": {},
}
)
self.assertDictEqual(cmd.script(name, onlyif=""), ret)
# 'call' function tests: 1
def test_call(self):
@ -262,24 +172,9 @@ class CmdTestCase(TestCase, LoaderModuleMockMixin):
self.assertDictEqual(cmd.call(name, func), ret)
flag = False
comt = "onlyif condition is false"
ret.update({"comment": "", "result": False, "changes": {"retval": []}})
self.assertDictEqual(cmd.call(name, func), ret)
mock = MagicMock(return_value=1)
with patch.dict(cmd.__salt__, {"cmd.retcode": mock}):
with patch.dict(cmd.__opts__, {"test": True}):
comt = "onlyif condition is false"
ret.update(
{
"comment": comt,
"skip_watch": True,
"result": True,
"changes": {},
}
)
self.assertDictEqual(cmd.call(name, func, onlyif=""), ret)
# 'wait_call' function tests: 1
def test_wait_call(self):

View file

@ -23,233 +23,215 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
"""
Test installing a PKG file
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "/path/to/file.pkg installed",
"name": "/path/to/file.pkg",
"result": True,
}
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "/path/to/file.pkg installed",
"name": "/path/to/file.pkg",
"result": True,
}
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
_mod_run_check_mock.return_value = True
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.pkg")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
install_mock.assert_called_once_with(
"/path/to/file.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.pkg")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
install_mock.assert_called_once_with(
"/path/to/file.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
def test_installed_pkg_exists(self):
"""
Test installing a PKG file where it's already installed
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {},
"comment": "",
"name": "/path/to/file.pkg",
"result": True,
}
expected = {
"changes": {},
"comment": "",
"name": "/path/to/file.pkg",
"result": True,
}
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
_mod_run_check_mock.return_value = True
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.pkg")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
assert not install_mock.called
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.pkg")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
assert not install_mock.called
self.assertEqual(out, expected)
def test_installed_pkg_version_succeeds(self):
"""
Test installing a PKG file where the version number matches the current installed version
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {},
"comment": "Version already matches .*5\\.1\\.[0-9]",
"name": "/path/to/file.pkg",
"result": True,
}
expected = {
"changes": {},
"comment": "Version already matches .*5\\.1\\.[0-9]",
"name": "/path/to/file.pkg",
"result": True,
}
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
cmd_mock = MagicMock(return_value="Version of this: 5.1.9")
_mod_run_check_mock.return_value = True
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
cmd_mock = MagicMock(return_value="Version of this: 5.1.9")
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
"cmd.run": cmd_mock,
},
):
out = macpackage.installed(
"/path/to/file.pkg",
version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]",
)
cmd_mock.assert_called_once_with(
"/usr/bin/runme --version",
output_loglevel="quiet",
ignore_retcode=True,
)
assert not installed_mock.called
assert not get_pkg_id_mock.called
assert not install_mock.called
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
"cmd.run": cmd_mock,
},
):
out = macpackage.installed(
"/path/to/file.pkg",
version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]",
)
cmd_mock.assert_called_once_with(
"/usr/bin/runme --version", output_loglevel="quiet", ignore_retcode=True
)
assert not installed_mock.called
assert not get_pkg_id_mock.called
assert not install_mock.called
self.assertEqual(out, expected)
def test_installed_pkg_version_fails(self):
"""
Test installing a PKG file where the version number if different from the expected one
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "Version Version of this: 1.8.9 doesn't match .*5\\.1\\.[0-9]. /path/to/file.pkg installed",
"name": "/path/to/file.pkg",
"result": True,
}
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "Version Version of this: 1.8.9 doesn't match .*5\\.1\\.[0-9]. /path/to/file.pkg installed",
"name": "/path/to/file.pkg",
"result": True,
}
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
cmd_mock = MagicMock(return_value="Version of this: 1.8.9")
_mod_run_check_mock.return_value = True
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
cmd_mock = MagicMock(return_value="Version of this: 1.8.9")
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
"cmd.run": cmd_mock,
},
):
out = macpackage.installed(
"/path/to/file.pkg",
version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]",
)
cmd_mock.assert_called_once_with(
"/usr/bin/runme --version",
output_loglevel="quiet",
ignore_retcode=True,
)
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
install_mock.assert_called_once_with(
"/path/to/file.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
"cmd.run": cmd_mock,
},
):
out = macpackage.installed(
"/path/to/file.pkg",
version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]",
)
cmd_mock.assert_called_once_with(
"/usr/bin/runme --version", output_loglevel="quiet", ignore_retcode=True
)
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg")
install_mock.assert_called_once_with(
"/path/to/file.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
def test_installed_dmg(self):
"""
Test installing a DMG file
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "/path/to/file.dmg installed",
"name": "/path/to/file.dmg",
"result": True,
}
expected = {
"changes": {"installed": ["some.other.id"]},
"comment": "/path/to/file.dmg installed",
"name": "/path/to/file.dmg",
"result": True,
}
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
_mod_run_check_mock.return_value = True
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
installed_mock = MagicMock(return_value=["com.apple.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macpackage.__salt__,
{
"macpackage.mount": mount_mock,
"macpackage.unmount": unmount_mock,
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.dmg", dmg=True)
mount_mock.assert_called_once_with("/path/to/file.dmg")
unmount_mock.assert_called_once_with("/tmp/dmg-X")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg")
install_mock.assert_called_once_with(
"/tmp/dmg-X/*.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.mount": mount_mock,
"macpackage.unmount": unmount_mock,
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.dmg", dmg=True)
mount_mock.assert_called_once_with("/path/to/file.dmg")
unmount_mock.assert_called_once_with("/tmp/dmg-X")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg")
install_mock.assert_called_once_with(
"/tmp/dmg-X/*.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
def test_installed_dmg_exists(self):
"""
Test installing a DMG file when the package already exists
"""
with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock:
expected = {
"changes": {},
"comment": "",
"name": "/path/to/file.dmg",
"result": True,
}
expected = {
"changes": {},
"comment": "",
"name": "/path/to/file.dmg",
"result": True,
}
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
_mod_run_check_mock.return_value = True
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"])
get_pkg_id_mock = MagicMock(return_value=["some.other.id"])
install_mock = MagicMock(return_value={"retcode": 0})
with patch.dict(
macpackage.__salt__,
{
"macpackage.mount": mount_mock,
"macpackage.unmount": unmount_mock,
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.dmg", dmg=True)
mount_mock.assert_called_once_with("/path/to/file.dmg")
unmount_mock.assert_called_once_with("/tmp/dmg-X")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg")
assert not install_mock.called
self.assertEqual(out, expected)
with patch.dict(
macpackage.__salt__,
{
"macpackage.mount": mount_mock,
"macpackage.unmount": unmount_mock,
"macpackage.installed_pkgs": installed_mock,
"macpackage.get_pkg_id": get_pkg_id_mock,
"macpackage.install": install_mock,
},
):
out = macpackage.installed("/path/to/file.dmg", dmg=True)
mount_mock.assert_called_once_with("/path/to/file.dmg")
unmount_mock.assert_called_once_with("/tmp/dmg-X")
installed_mock.assert_called_once_with()
get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg")
assert not install_mock.called
self.assertEqual(out, expected)
def test_installed_app(self):
"""
Test installing an APP file
"""
with patch(
"salt.states.macpackage._mod_run_check"
) as _mod_run_check_mock, patch("os.path.exists") as exists_mock:
with patch("os.path.exists") as exists_mock:
expected = {
"changes": {"installed": ["file.app"]},
"comment": "file.app installed",
@ -258,7 +240,6 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
}
install_mock = MagicMock()
_mod_run_check_mock.return_value = True
exists_mock.return_value = False
with patch.dict(
@ -275,9 +256,7 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
"""
Test installing an APP file that already exists
"""
with patch(
"salt.states.macpackage._mod_run_check"
) as _mod_run_check_mock, patch("os.path.exists") as exists_mock:
with patch("os.path.exists") as exists_mock:
expected = {
"changes": {},
"comment": "",
@ -286,7 +265,6 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
}
install_mock = MagicMock()
_mod_run_check_mock.return_value = True
exists_mock.return_value = True
with patch.dict(
@ -301,9 +279,7 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
"""
Test installing an APP file contained in a DMG file
"""
with patch(
"salt.states.macpackage._mod_run_check"
) as _mod_run_check_mock, patch("os.path.exists") as exists_mock:
with patch("os.path.exists") as exists_mock:
expected = {
"changes": {"installed": ["file.app"]},
"comment": "file.app installed",
@ -315,7 +291,6 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
cmd_mock = MagicMock(return_value="file.app")
_mod_run_check_mock.return_value = True
exists_mock.return_value = False
with patch.dict(
@ -343,9 +318,7 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
"""
Test installing an APP file contained in a DMG file where the file exists
"""
with patch(
"salt.states.macpackage._mod_run_check"
) as _mod_run_check_mock, patch("os.path.exists") as exists_mock:
with patch("os.path.exists") as exists_mock:
expected = {
"changes": {},
"comment": "",
@ -357,7 +330,6 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"])
unmount_mock = MagicMock()
cmd_mock = MagicMock(return_value="file.app")
_mod_run_check_mock.return_value = True
exists_mock.return_value = True
with patch.dict(
@ -411,39 +383,3 @@ class MacPackageTestCase(TestCase, LoaderModuleMockMixin):
"/path/to/file.pkg", "LocalSystem", False, False
)
self.assertEqual(out, expected)
def test_installed_pkg_onlyif_fail(self,):
"""
Test installing a PKG file where the onlyif call fails
"""
expected = {
"changes": {},
"comment": "onlyif condition is false",
"skip_watch": True,
"result": True,
"name": "/path/to/file.pkg",
}
mock = MagicMock(return_value=1)
with patch.dict(macpackage.__salt__, {"cmd.retcode": mock}):
out = macpackage.installed("/path/to/file.pkg", onlyif="some command")
self.assertEqual(out, expected)
def test_installed_pkg_unless_fail(self,):
"""
Test installing a PKG file where the unless run fails
"""
expected = {
"changes": {},
"comment": "unless condition is true",
"skip_watch": True,
"result": True,
"name": "/path/to/file.pkg",
}
mock = MagicMock(return_value=0)
with patch.dict(macpackage.__salt__, {"cmd.retcode": mock}):
out = macpackage.installed("/path/to/file.pkg", unless="some command")
self.assertEqual(out, expected)

View file

@ -15,6 +15,7 @@ import salt.exceptions
import salt.state
import salt.utils.files
import salt.utils.platform
from salt.exceptions import CommandExecutionError
from salt.utils.decorators import state as statedecorators
from salt.utils.odict import OrderedDict
from tests.support.helpers import with_tempfile
@ -120,6 +121,70 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
return_result = state_obj._run_check_onlyif(low_data, "")
self.assertEqual(expected_result, return_result)
def test_verify_onlyif_cmd_error(self):
"""
Simulates a failure in cmd.retcode from onlyif
This could occur is runas is specified with a user that does not exist
"""
low_data = {
"onlyif": "somecommand",
"runas" "doesntexist" "name": "echo something",
"state": "cmd",
"__id__": "this is just a test",
"fun": "run",
"__env__": "base",
"__sls__": "sometest",
"order": 10000,
}
expected_result = {
"comment": "onlyif condition is false",
"result": True,
"skip_watch": True,
}
with patch("salt.state.State._gather_pillar") as state_patch:
minion_opts = self.get_temp_config("minion")
state_obj = salt.state.State(minion_opts)
mock = MagicMock(side_effect=CommandExecutionError("Boom!"))
with patch.dict(state_obj.functions, {"cmd.retcode": mock}):
# The mock handles the exception, but the runas dict is being passed as it would actually be
return_result = state_obj._run_check_onlyif(
low_data, {"runas": "doesntexist"}
)
self.assertEqual(expected_result, return_result)
def test_verify_unless_cmd_error(self):
"""
Simulates a failure in cmd.retcode from unless
This could occur is runas is specified with a user that does not exist
"""
low_data = {
"unless": "somecommand",
"runas" "doesntexist" "name": "echo something",
"state": "cmd",
"__id__": "this is just a test",
"fun": "run",
"__env__": "base",
"__sls__": "sometest",
"order": 10000,
}
expected_result = {
"comment": "unless condition is true",
"result": True,
"skip_watch": True,
}
with patch("salt.state.State._gather_pillar") as state_patch:
minion_opts = self.get_temp_config("minion")
state_obj = salt.state.State(minion_opts)
mock = MagicMock(side_effect=CommandExecutionError("Boom!"))
with patch.dict(state_obj.functions, {"cmd.retcode": mock}):
# The mock handles the exception, but the runas dict is being passed as it would actually be
return_result = state_obj._run_check_unless(
low_data, {"runas": "doesntexist"}
)
self.assertEqual(expected_result, return_result)
def test_verify_unless_parse(self):
low_data = {
"unless": [{"fun": "test.arg", "args": ["arg1", "arg2"]}],
@ -146,6 +211,72 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
return_result = state_obj._run_check_unless(low_data, "")
self.assertEqual(expected_result, return_result)
def test_verify_creates(self):
low_data = {
"state": "cmd",
"name": 'echo "something"',
"__sls__": "tests.creates",
"__env__": "base",
"__id__": "do_a_thing",
"creates": "/tmp/thing",
"order": 10000,
"fun": "run",
}
with patch("salt.state.State._gather_pillar") as state_patch:
minion_opts = self.get_temp_config("minion")
state_obj = salt.state.State(minion_opts)
with patch("os.path.exists") as path_mock:
path_mock.return_value = True
expected_result = {
"comment": "/tmp/thing exists",
"result": True,
"skip_watch": True,
}
return_result = state_obj._run_check_creates(low_data)
self.assertEqual(expected_result, return_result)
path_mock.return_value = False
expected_result = {
"comment": "Creates files not found",
"result": False,
}
return_result = state_obj._run_check_creates(low_data)
self.assertEqual(expected_result, return_result)
def test_verify_creates_list(self):
low_data = {
"state": "cmd",
"name": 'echo "something"',
"__sls__": "tests.creates",
"__env__": "base",
"__id__": "do_a_thing",
"creates": ["/tmp/thing", "/tmp/thing2"],
"order": 10000,
"fun": "run",
}
with patch("salt.state.State._gather_pillar") as state_patch:
minion_opts = self.get_temp_config("minion")
state_obj = salt.state.State(minion_opts)
with patch("os.path.exists") as path_mock:
path_mock.return_value = True
expected_result = {
"comment": "All files in creates exist",
"result": True,
"skip_watch": True,
}
return_result = state_obj._run_check_creates(low_data)
self.assertEqual(expected_result, return_result)
path_mock.return_value = False
expected_result = {
"comment": "Creates files not found",
"result": False,
}
return_result = state_obj._run_check_creates(low_data)
self.assertEqual(expected_result, return_result)
def _expand_win_path(self, path):
"""
Expand C:/users/admini~1/appdata/local/temp/salt-tests-tmpdir/...
@ -193,6 +324,28 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
return_result = state_obj._run_check_onlyif(low_data, "")
self.assertEqual(expected_result, return_result)
def test_verify_onlyif_list_cmd(self):
low_data = {
"state": "cmd",
"name": 'echo "something"',
"__sls__": "tests.cmd",
"__env__": "base",
"__id__": "check onlyif",
"onlyif": ["/bin/true", "/bin/false"],
"order": 10001,
"fun": "run",
}
expected_result = {
"comment": "onlyif condition is false",
"result": True,
"skip_watch": True,
}
with patch("salt.state.State._gather_pillar") as state_patch:
minion_opts = self.get_temp_config("minion")
state_obj = salt.state.State(minion_opts)
return_result = state_obj._run_check_onlyif(low_data, {})
self.assertEqual(expected_result, return_result)
@with_tempfile()
def test_verify_unless_parse_slots(self, name):
with salt.utils.files.fopen(name, "w") as fp: