2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
:codeauthor: Nicole Thomas <nicole@saltstack.com>
|
|
|
|
"""
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
import pytest
|
2022-07-20 10:42:30 +01:00
|
|
|
|
2021-08-12 11:38:23 -07:00
|
|
|
import salt.exceptions
|
|
|
|
import salt.state
|
|
|
|
import salt.utils.files
|
|
|
|
import salt.utils.platform
|
|
|
|
from salt.exceptions import CommandExecutionError
|
|
|
|
from salt.utils.odict import OrderedDict
|
|
|
|
from tests.support.mock import MagicMock, patch
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2023-04-15 20:19:58 -05:00
|
|
|
pytestmark = [
|
|
|
|
pytest.mark.core_test,
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2021-08-12 11:38:23 -07:00
|
|
|
def test_format_log_non_ascii_character():
|
|
|
|
"""
|
|
|
|
Tests running a non-ascii character through the state.format_log
|
|
|
|
function. See Issue #33605.
|
|
|
|
"""
|
|
|
|
# There is no return to test against as the format_log
|
|
|
|
# function doesn't return anything. However, we do want
|
|
|
|
# to make sure that the function doesn't stacktrace when
|
|
|
|
# called.
|
|
|
|
ret = {
|
|
|
|
"changes": {"Français": {"old": "something old", "new": "something new"}},
|
|
|
|
"result": True,
|
|
|
|
}
|
|
|
|
salt.state.format_log(ret)
|
|
|
|
|
|
|
|
|
2023-09-12 13:33:11 -06:00
|
|
|
def test_format_log_list(caplog):
|
|
|
|
"""
|
|
|
|
Test running format_log when ret is not a dictionary
|
|
|
|
"""
|
|
|
|
ret = ["test1", "test2"]
|
|
|
|
salt.state.format_log(ret)
|
|
|
|
assert "INFO" in caplog.text
|
|
|
|
assert f"{ret}" in caplog.text
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_render_error_on_invalid_requisite(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Test that the state compiler correctly deliver a rendering
|
|
|
|
exception when a requisite cannot be resolved
|
|
|
|
"""
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
high_data = {
|
|
|
|
"git": OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"pkg",
|
|
|
|
[
|
|
|
|
OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"require",
|
|
|
|
[
|
|
|
|
OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"file",
|
|
|
|
OrderedDict(
|
|
|
|
[("test1", "test")]
|
|
|
|
),
|
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
],
|
|
|
|
)
|
|
|
|
]
|
|
|
|
),
|
|
|
|
"installed",
|
|
|
|
{"order": 10000},
|
|
|
|
],
|
|
|
|
),
|
|
|
|
("__sls__", "issue_35226"),
|
|
|
|
("__env__", "base"),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
minion_opts["pillar"] = {"git": OrderedDict([("test1", "test")])}
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
with pytest.raises(salt.exceptions.SaltRenderError):
|
|
|
|
state_obj.call_high(high_data)
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_parse(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"onlyif": [{"fun": "test.arg", "args": ["arg1", "arg2"]}],
|
|
|
|
"name": "mysql-server-5.7",
|
|
|
|
"state": "debconf",
|
|
|
|
"__id__": "set root password",
|
|
|
|
"fun": "set",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "debconf",
|
|
|
|
"data": {
|
|
|
|
"mysql-server/root_password": {"type": "password", "value": "temp123"}
|
|
|
|
},
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "onlyif condition is true", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_parse_deep_return(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "test",
|
|
|
|
"name": "foo",
|
|
|
|
"__sls__": "consol",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "test",
|
|
|
|
"onlyif": [
|
|
|
|
{
|
|
|
|
"fun": "test.arg",
|
|
|
|
"get_return": "kwargs:deep:return",
|
|
|
|
"deep": {"return": "true"},
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "nop",
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "onlyif condition is true", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_cmd_error(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Simulates a failure in cmd.retcode from onlyif
|
|
|
|
This could occur if 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,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
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"}
|
|
|
|
)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_cmd_error(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Simulates a failure in cmd.retcode from unless
|
|
|
|
This could occur if 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,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
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"}
|
|
|
|
)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_list_cmd(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
If any of the unless commands return False (non 0) then the state should
|
|
|
|
run (no skip_watch).
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check unless",
|
|
|
|
"unless": ["exit 0", "exit 1"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "unless condition is false",
|
|
|
|
"result": False,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_list_cmd_different_order(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
If any of the unless commands return False (non 0) then the state should
|
|
|
|
run (no skip_watch). The order shouldn't matter.
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check unless",
|
|
|
|
"unless": ["exit 1", "exit 0"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "unless condition is false",
|
|
|
|
"result": False,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_list_cmd_different_order(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check onlyif",
|
|
|
|
"onlyif": ["exit 1", "exit 0"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "onlyif condition is false",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_list_cmd_valid(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
If any of the unless commands return False (non 0) then the state should
|
|
|
|
run (no skip_watch). This tests all commands return False.
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check unless",
|
|
|
|
"unless": ["exit 1", "exit 1"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "unless condition is false", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_list_cmd_valid(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check onlyif",
|
|
|
|
"onlyif": ["exit 0", "exit 0"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "onlyif condition is true", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_list_cmd_invalid(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
If any of the unless commands return False (non 0) then the state should
|
|
|
|
run (no skip_watch). This tests all commands return True
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check unless",
|
|
|
|
"unless": ["exit 0", "exit 0"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "unless condition is true",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_list_cmd_invalid(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check onlyif",
|
|
|
|
"onlyif": ["exit 1", "exit 1"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "onlyif condition is false",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_parse(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"unless": [{"fun": "test.arg", "args": ["arg1", "arg2"]}],
|
|
|
|
"name": "mysql-server-5.7",
|
|
|
|
"state": "debconf",
|
|
|
|
"__id__": "set root password",
|
|
|
|
"fun": "set",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "debconf",
|
|
|
|
"data": {
|
|
|
|
"mysql-server/root_password": {"type": "password", "value": "temp123"}
|
|
|
|
},
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "unless condition is true",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_parse_deep_return(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "test",
|
|
|
|
"name": "foo",
|
|
|
|
"__sls__": "consol",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "test",
|
|
|
|
"unless": [
|
|
|
|
{
|
|
|
|
"fun": "test.arg",
|
|
|
|
"get_return": "kwargs:deep:return",
|
|
|
|
"deep": {"return": False},
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "nop",
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "unless condition is false", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_creates(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.creates",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "do_a_thing",
|
|
|
|
"creates": "/tmp/thing",
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
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,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
return_result = state_obj._run_check_creates(low_data)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
path_mock.return_value = False
|
|
|
|
expected_result = {
|
|
|
|
"comment": "Creates files not found",
|
|
|
|
"result": False,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
return_result = state_obj._run_check_creates(low_data)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_creates_list(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
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",
|
|
|
|
}
|
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
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,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
return_result = state_obj._run_check_creates(low_data)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
path_mock.return_value = False
|
|
|
|
expected_result = {
|
|
|
|
"comment": "Creates files not found",
|
|
|
|
"result": False,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
return_result = state_obj._run_check_creates(low_data)
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
|
|
|
def _expand_win_path(path):
|
|
|
|
"""
|
|
|
|
Expand C:/users/admini~1/appdata/local/temp/salt-tests-tmpdir/...
|
|
|
|
into C:/users/adminitrator/appdata/local/temp/salt-tests-tmpdir/...
|
|
|
|
to prevent file.search from expanding the "~" with os.path.expanduser
|
|
|
|
"""
|
|
|
|
if salt.utils.platform.is_windows():
|
|
|
|
import win32file
|
|
|
|
|
|
|
|
return win32file.GetLongPathName(path).replace("\\", "/")
|
|
|
|
else:
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_parse_slots(tmp_path, minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
name = str(tmp_path / "testfile.txt")
|
|
|
|
with salt.utils.files.fopen(name, "w") as fp:
|
|
|
|
fp.write("file-contents")
|
|
|
|
low_data = {
|
|
|
|
"onlyif": [
|
|
|
|
{
|
|
|
|
"fun": "file.search",
|
2023-06-29 09:41:56 +01:00
|
|
|
"args": [f"__slot__:salt:test.echo({_expand_win_path(name)})"],
|
2021-08-12 11:38:23 -07:00
|
|
|
"pattern": "__slot__:salt:test.echo(file-contents)",
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"name": "mysql-server-5.7",
|
|
|
|
"state": "debconf",
|
|
|
|
"__id__": "set root password",
|
|
|
|
"fun": "set",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "debconf",
|
|
|
|
"data": {
|
|
|
|
"mysql-server/root_password": {"type": "password", "value": "temp123"}
|
|
|
|
},
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
expected_result = {"comment": "onlyif condition is true", "result": False}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_list_cmd(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "cmd",
|
|
|
|
"name": 'echo "something"',
|
|
|
|
"__sls__": "tests.cmd",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "check onlyif",
|
|
|
|
"onlyif": ["exit 0", "exit 1"],
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "run",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "onlyif condition is false",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_onlyif(low_data, {})
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_onlyif_cmd_args(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Verify cmd.run state arguments are properly passed to cmd.retcode in onlyif
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"onlyif": "somecommand",
|
|
|
|
"cwd": "acwd",
|
|
|
|
"root": "aroot",
|
|
|
|
"env": [{"akey": "avalue"}],
|
|
|
|
"prepend_path": "apath",
|
|
|
|
"umask": "0700",
|
|
|
|
"success_retcodes": 1,
|
|
|
|
"timeout": 5,
|
|
|
|
"runas": "doesntexist",
|
|
|
|
"name": "echo something",
|
|
|
|
"shell": "/bin/dash",
|
|
|
|
"state": "cmd",
|
|
|
|
"__id__": "this is just a test",
|
|
|
|
"fun": "run",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "sometest",
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
mock = MagicMock()
|
|
|
|
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(low_data)
|
|
|
|
mock.assert_called_once_with(
|
|
|
|
"somecommand",
|
|
|
|
ignore_retcode=True,
|
|
|
|
python_shell=True,
|
|
|
|
cwd="acwd",
|
|
|
|
root="aroot",
|
|
|
|
runas="doesntexist",
|
|
|
|
env=[{"akey": "avalue"}],
|
|
|
|
prepend_path="apath",
|
|
|
|
umask="0700",
|
|
|
|
timeout=5,
|
|
|
|
success_retcodes=1,
|
|
|
|
shell="/bin/dash",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_unless_parse_slots(tmp_path, minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
name = str(tmp_path / "testfile.txt")
|
|
|
|
with salt.utils.files.fopen(name, "w") as fp:
|
|
|
|
fp.write("file-contents")
|
|
|
|
low_data = {
|
|
|
|
"unless": [
|
|
|
|
{
|
|
|
|
"fun": "file.search",
|
2023-06-29 09:41:56 +01:00
|
|
|
"args": [f"__slot__:salt:test.echo({_expand_win_path(name)})"],
|
2021-08-12 11:38:23 -07:00
|
|
|
"pattern": "__slot__:salt:test.echo(file-contents)",
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"name": "mysql-server-5.7",
|
|
|
|
"state": "debconf",
|
|
|
|
"__id__": "set root password",
|
|
|
|
"fun": "set",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "debconf",
|
|
|
|
"data": {
|
|
|
|
"mysql-server/root_password": {"type": "password", "value": "temp123"}
|
|
|
|
},
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"comment": "unless condition is true",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
return_result = state_obj._run_check_unless(low_data, "")
|
|
|
|
assert expected_result == return_result
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_verify_retry_parsing(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
low_data = {
|
|
|
|
"state": "file",
|
|
|
|
"name": "/tmp/saltstack.README.rst",
|
|
|
|
"__sls__": "demo.download",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "download sample data",
|
|
|
|
"retry": {"attempts": 5, "interval": 5},
|
|
|
|
"unless": ["test -f /tmp/saltstack.README.rst"],
|
|
|
|
"source": [
|
|
|
|
"https://raw.githubusercontent.com/saltstack/salt/develop/README.rst"
|
|
|
|
],
|
|
|
|
"source_hash": "f2bc8c0aa2ae4f5bb5c2051686016b48",
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "managed",
|
|
|
|
}
|
|
|
|
expected_result = {
|
|
|
|
"__run_num__": 0,
|
|
|
|
"changes": {},
|
|
|
|
"comment": "['unless condition is true'] The state would be retried every 5 "
|
|
|
|
"seconds (with a splay of up to 0 seconds) a maximum of 5 times or "
|
|
|
|
"until a result of True is returned",
|
|
|
|
"result": True,
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
2022-11-04 05:05:41 +00:00
|
|
|
for key in ("__sls__", "__id__", "name"):
|
|
|
|
expected_result[key] = low_data.get(key)
|
2021-08-12 11:38:23 -07:00
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
minion_opts["test"] = True
|
|
|
|
minion_opts["file_client"] = "local"
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
mock = {
|
|
|
|
"result": True,
|
|
|
|
"comment": ["unless condition is true"],
|
|
|
|
"skip_watch": True,
|
|
|
|
}
|
|
|
|
with patch.object(state_obj, "_run_check", return_value=mock):
|
|
|
|
assert set(expected_result).issubset(set(state_obj.call(low_data)))
|
|
|
|
|
|
|
|
|
2021-05-31 18:54:59 +01:00
|
|
|
def test_render_requisite_require_disabled(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Test that the state compiler correctly deliver a rendering
|
|
|
|
exception when a requisite cannot be resolved
|
|
|
|
"""
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
high_data = {
|
|
|
|
"step_one": OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"test",
|
|
|
|
[
|
|
|
|
OrderedDict(
|
|
|
|
[("require", [OrderedDict([("test", "step_two")])])]
|
|
|
|
),
|
|
|
|
"succeed_with_changes",
|
|
|
|
{"order": 10000},
|
|
|
|
],
|
|
|
|
),
|
|
|
|
("__sls__", "test.disable_require"),
|
|
|
|
("__env__", "base"),
|
|
|
|
]
|
|
|
|
),
|
|
|
|
"step_two": {
|
|
|
|
"test": ["succeed_with_changes", {"order": 10001}],
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "test.disable_require",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
minion_opts["disabled_requisites"] = ["require"]
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
ret = state_obj.call_high(high_data)
|
|
|
|
run_num = ret["test_|-step_one_|-step_one_|-succeed_with_changes"][
|
|
|
|
"__run_num__"
|
|
|
|
]
|
|
|
|
assert run_num == 0
|
|
|
|
|
|
|
|
|
2021-05-31 18:54:59 +01:00
|
|
|
def test_render_requisite_require_in_disabled(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Test that the state compiler correctly deliver a rendering
|
|
|
|
exception when a requisite cannot be resolved
|
|
|
|
"""
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
high_data = {
|
|
|
|
"step_one": {
|
|
|
|
"test": ["succeed_with_changes", {"order": 10000}],
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "test.disable_require_in",
|
|
|
|
},
|
|
|
|
"step_two": OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"test",
|
|
|
|
[
|
|
|
|
OrderedDict(
|
|
|
|
[
|
|
|
|
(
|
|
|
|
"require_in",
|
|
|
|
[OrderedDict([("test", "step_one")])],
|
|
|
|
)
|
|
|
|
]
|
|
|
|
),
|
|
|
|
"succeed_with_changes",
|
|
|
|
{"order": 10001},
|
|
|
|
],
|
|
|
|
),
|
|
|
|
("__sls__", "test.disable_require_in"),
|
|
|
|
("__env__", "base"),
|
|
|
|
]
|
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
minion_opts["disabled_requisites"] = ["require_in"]
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
ret = state_obj.call_high(high_data)
|
|
|
|
run_num = ret["test_|-step_one_|-step_one_|-succeed_with_changes"][
|
|
|
|
"__run_num__"
|
|
|
|
]
|
|
|
|
assert run_num == 0
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_call_chunk_sub_state_run(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Test running a batch of states with an external runner
|
|
|
|
that returns sub_state_run
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"state": "external",
|
|
|
|
"name": "external_state_name",
|
|
|
|
"__id__": "do_a_thing",
|
|
|
|
"__sls__": "external",
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "state",
|
|
|
|
}
|
|
|
|
mock_call_return = {
|
|
|
|
"__run_num__": 0,
|
|
|
|
"sub_state_run": [
|
|
|
|
{
|
|
|
|
"changes": {},
|
|
|
|
"result": True,
|
|
|
|
"comment": "",
|
|
|
|
"low": {
|
|
|
|
"name": "external_state_name",
|
|
|
|
"__id__": "external_state_id",
|
|
|
|
"state": "external_state",
|
|
|
|
"fun": "external_function",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
],
|
|
|
|
}
|
|
|
|
expected_sub_state_tag = (
|
|
|
|
"external_state_|-external_state_id_|-external_state_name_|-external_function"
|
|
|
|
)
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
with patch("salt.state.State.call", return_value=mock_call_return):
|
|
|
|
minion_opts["disabled_requisites"] = ["require"]
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
ret = state_obj.call_chunk(low_data, {}, {})
|
|
|
|
sub_state = ret.get(expected_sub_state_tag)
|
|
|
|
assert sub_state
|
|
|
|
assert sub_state["__run_num__"] == 1
|
|
|
|
assert sub_state["name"] == "external_state_name"
|
|
|
|
assert sub_state["__state_ran__"]
|
|
|
|
assert sub_state["__sls__"] == "external"
|
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_aggregate_requisites(minion_opts):
|
2021-08-12 11:38:23 -07:00
|
|
|
"""
|
|
|
|
Test to ensure that the requisites are included in the aggregated low state.
|
|
|
|
"""
|
|
|
|
# The low that is returned from _mod_aggregrate
|
|
|
|
low = {
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "other_pkgs",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "other_pkgs",
|
|
|
|
"pkgs": ["byobu", "vim", "tmux", "google-cloud-sdk"],
|
|
|
|
"aggregate": True,
|
|
|
|
"order": 10002,
|
|
|
|
"fun": "installed",
|
|
|
|
"__agg__": True,
|
|
|
|
}
|
|
|
|
|
|
|
|
# Chunks that have been processed through the pkg mod_aggregate function
|
|
|
|
chunks = [
|
|
|
|
{
|
|
|
|
"state": "file",
|
|
|
|
"name": "/tmp/install-vim",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "/tmp/install-vim",
|
|
|
|
"order": 10000,
|
|
|
|
"fun": "managed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "file",
|
|
|
|
"name": "/tmp/install-tmux",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "/tmp/install-tmux",
|
|
|
|
"order": 10001,
|
|
|
|
"fun": "managed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "other_pkgs",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env __": "base",
|
|
|
|
"__id__": "other_pkgs",
|
|
|
|
"pkgs": ["byobu"],
|
|
|
|
"aggregate": True,
|
|
|
|
"order": 10002,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "bc",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "bc",
|
|
|
|
"hold": True,
|
|
|
|
"__agg__": True,
|
|
|
|
"order": 10003,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "vim",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__agg__": True,
|
|
|
|
"__id__": "vim",
|
|
|
|
"require": ["/tmp/install-vim"],
|
|
|
|
"order": 10004,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "tmux",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__agg__": True,
|
|
|
|
"__id__": "tmux",
|
|
|
|
"require": ["/tmp/install-tmux"],
|
|
|
|
"order": 10005,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkgrepo",
|
|
|
|
"name": "deb https://packages.cloud.google.com/apt cloud-sdk main",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "google-cloud-repo",
|
|
|
|
"humanname": "Google Cloud SDK",
|
|
|
|
"file": "/etc/apt/sources.list.d/google-cloud-sdk.list",
|
|
|
|
"key_url": "https://packages.cloud.google.com/apt/doc/apt-key.gpg",
|
|
|
|
"order": 10006,
|
|
|
|
"fun": "managed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "google-cloud-sdk",
|
|
|
|
"__sls__": "47628",
|
|
|
|
"__env__": "base",
|
|
|
|
"__agg__": True,
|
|
|
|
"__id__": "google-cloud-sdk",
|
|
|
|
"require": ["google-cloud-repo"],
|
|
|
|
"order": 10007,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2021-08-12 11:38:23 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
low_ret = state_obj._aggregate_requisites(low, chunks)
|
|
|
|
|
|
|
|
# Ensure the low returned contains require
|
|
|
|
assert "require" in low_ret
|
|
|
|
|
|
|
|
# Ensure all the requires from pkg states are in low
|
|
|
|
assert low_ret["require"] == [
|
|
|
|
"/tmp/install-vim",
|
|
|
|
"/tmp/install-tmux",
|
|
|
|
"google-cloud-repo",
|
|
|
|
]
|
2022-08-24 15:57:27 -07:00
|
|
|
|
|
|
|
|
2022-11-13 11:07:48 +00:00
|
|
|
def test_mod_aggregate(minion_opts):
|
2022-08-24 15:57:27 -07:00
|
|
|
"""
|
|
|
|
Test to ensure that the requisites are included in the aggregated low state.
|
|
|
|
"""
|
|
|
|
# The low that is returned from _mod_aggregrate
|
|
|
|
low = {
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "sl",
|
|
|
|
"__sls__": "test.62439",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "sl",
|
|
|
|
"require_in": [OrderedDict([("file", "/tmp/foo")])],
|
|
|
|
"order": 10002,
|
|
|
|
"aggregate": True,
|
|
|
|
"fun": "installed",
|
|
|
|
}
|
|
|
|
|
|
|
|
# Chunks that have been processed through the pkg mod_aggregate function
|
|
|
|
chunks = [
|
|
|
|
{
|
|
|
|
"state": "file",
|
|
|
|
"name": "/tmp/foo",
|
|
|
|
"__sls__": "test.62439",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "/tmp/foo",
|
|
|
|
"content": "This is some content",
|
|
|
|
"order": 10000,
|
|
|
|
"require": [{"pkg": "sl"}],
|
|
|
|
"fun": "managed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "figlet",
|
|
|
|
"__sls__": "test.62439",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "figlet",
|
|
|
|
"__agg__": True,
|
|
|
|
"require": [OrderedDict([("file", "/tmp/foo")])],
|
|
|
|
"order": 10001,
|
|
|
|
"aggregate": True,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "sl",
|
|
|
|
"__sls__": "test.62439",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "sl",
|
|
|
|
"require_in": [OrderedDict([("file", "/tmp/foo")])],
|
|
|
|
"order": 10002,
|
|
|
|
"aggregate": True,
|
|
|
|
"fun": "installed",
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
running = {}
|
|
|
|
|
|
|
|
mock_pkg_mod_aggregate = {
|
|
|
|
"state": "pkg",
|
|
|
|
"name": "sl",
|
|
|
|
"__sls__": "test.62439",
|
|
|
|
"__env__": "base",
|
|
|
|
"__id__": "sl",
|
|
|
|
"require_in": [OrderedDict([("file", "/tmp/foo")])],
|
|
|
|
"order": 10002,
|
|
|
|
"fun": "installed",
|
|
|
|
"__agg__": True,
|
|
|
|
"pkgs": ["figlet", "sl"],
|
|
|
|
}
|
|
|
|
|
2022-11-04 05:05:41 +00:00
|
|
|
with patch("salt.state.State._gather_pillar"):
|
2022-08-24 15:57:27 -07:00
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
with patch.dict(
|
|
|
|
state_obj.states,
|
|
|
|
{"pkg.mod_aggregate": MagicMock(return_value=mock_pkg_mod_aggregate)},
|
|
|
|
):
|
|
|
|
low_ret = state_obj._mod_aggregate(low, running, chunks)
|
|
|
|
|
|
|
|
# Ensure the low returned contains require
|
|
|
|
assert "require_in" in low_ret
|
|
|
|
|
|
|
|
# Ensure all the requires from pkg states are in low
|
|
|
|
assert low_ret["require_in"] == [OrderedDict([("file", "/tmp/foo")])]
|
|
|
|
|
|
|
|
# Ensure that the require requisite from the
|
|
|
|
# figlet state doesn't find its way into this state
|
|
|
|
assert "require" not in low_ret
|
|
|
|
|
|
|
|
# Ensure pkgs were aggregated
|
|
|
|
assert low_ret["pkgs"] == ["figlet", "sl"]
|
2023-03-27 18:15:29 -07:00
|
|
|
|
|
|
|
|
|
|
|
def test_verify_onlyif_cmd_opts_exclude(minion_opts):
|
|
|
|
"""
|
|
|
|
Verify cmd.run state arguments are properly excluded from cmd.retcode
|
|
|
|
when passed.
|
|
|
|
"""
|
|
|
|
low_data = {
|
|
|
|
"onlyif": "somecommand",
|
|
|
|
"cmd_opts_exclude": ["shell"],
|
|
|
|
"cwd": "acwd",
|
|
|
|
"root": "aroot",
|
|
|
|
"env": [{"akey": "avalue"}],
|
|
|
|
"prepend_path": "apath",
|
|
|
|
"umask": "0700",
|
|
|
|
"success_retcodes": 1,
|
|
|
|
"timeout": 5,
|
|
|
|
"runas": "doesntexist",
|
|
|
|
"name": "echo something",
|
|
|
|
"shell": "/bin/dash",
|
|
|
|
"state": "cmd",
|
|
|
|
"__id__": "this is just a test",
|
|
|
|
"fun": "run",
|
|
|
|
"__env__": "base",
|
|
|
|
"__sls__": "sometest",
|
|
|
|
"order": 10000,
|
|
|
|
}
|
|
|
|
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
|
|
|
state_obj = salt.state.State(minion_opts)
|
|
|
|
mock = MagicMock()
|
|
|
|
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(low_data)
|
|
|
|
mock.assert_called_once_with(
|
|
|
|
"somecommand",
|
|
|
|
ignore_retcode=True,
|
|
|
|
python_shell=True,
|
|
|
|
cwd="acwd",
|
|
|
|
root="aroot",
|
|
|
|
runas="doesntexist",
|
|
|
|
env=[{"akey": "avalue"}],
|
|
|
|
prepend_path="apath",
|
|
|
|
umask="0700",
|
|
|
|
timeout=5,
|
|
|
|
success_retcodes=1,
|
|
|
|
)
|
2023-09-19 00:14:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("verifier", (salt.state.State, salt.state.Compiler))
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"high,err_msg",
|
|
|
|
(
|
|
|
|
(
|
|
|
|
{"/some/file": {"file.managed": ["source:salt://bla"]}},
|
2023-09-25 22:53:21 +02:00
|
|
|
"Too many functions declared in state '/some/file' in SLS 'sls'. Please choose one of the following: managed, source:salt://bla",
|
2023-09-19 00:14:19 +02:00
|
|
|
),
|
|
|
|
(
|
|
|
|
{"/some/file": {"file": ["managed", "source:salt://bla"]}},
|
2023-09-25 22:53:21 +02:00
|
|
|
"Too many functions declared in state '/some/file' in SLS 'sls'. Please choose one of the following: managed, source:salt://bla",
|
2023-09-19 00:14:19 +02:00
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
def test_verify_high_too_many_functions_declared_error_message(
|
|
|
|
high, err_msg, minion_opts, verifier
|
|
|
|
):
|
|
|
|
"""
|
|
|
|
Ensure the error message when a list item of a state call is
|
|
|
|
accidentally passed as a string instead of a single-item dict
|
|
|
|
is more meaningful. Example:
|
|
|
|
|
|
|
|
/some/file:
|
|
|
|
file.managed:
|
|
|
|
- source:salt://bla
|
|
|
|
|
|
|
|
/some/file:
|
|
|
|
file:
|
|
|
|
- managed
|
|
|
|
- source:salt://bla
|
|
|
|
|
|
|
|
Issue #38098.
|
|
|
|
"""
|
|
|
|
high[next(iter(high))]["__sls__"] = "sls"
|
|
|
|
with patch("salt.state.State._gather_pillar"):
|
|
|
|
if verifier is salt.state.Compiler:
|
|
|
|
state_obj = verifier(minion_opts, [])
|
|
|
|
else:
|
|
|
|
state_obj = verifier(minion_opts)
|
|
|
|
res = state_obj.verify_high(high)
|
|
|
|
assert isinstance(res, list)
|
|
|
|
assert any(err_msg in x for x in res)
|