salt/tests/pytests/unit/state/test_reactor_compiler.py
bdrx312 66caa58346 Fix issues with requisites and aggregate
add install of networkx
fix aggregate to properly work with requisites
fix requisite checking to not be exponential
fix pkg aggregate to work when multiple states specify the same package
add some type hints to state.py to make the code easier to follow
fix case of pkg aggregate duplicate package.
2024-10-10 01:53:52 -07:00

608 lines
22 KiB
Python

import logging
import pytest
import salt.minion
import salt.state
from salt.utils.odict import OrderedDict
from tests.support.mock import MagicMock, patch
log = logging.getLogger(__name__)
pytestmark = [
pytest.mark.core_test,
]
def test_compiler_render_template(minion_opts, tmp_path):
"""
Test Compiler.render_template
"""
minion = "poc-minion"
kwargs = {
"tag": f"salt/minion/{minion}/start",
"data": {
"id": minion,
"cmd": "_minion_event",
"pretag": None,
"data": f"Minion {minion} started at Thu Sep 14 07:31:04 2023",
"tag": f"salt/minion/{minion}/start",
"_stamp": "2023-09-14T13:31:05.000316",
},
}
reactor_file = tmp_path / "reactor.sls"
content = f"""
highstate_run:
local.state.apply:
- tgt: {minion}
- args:
- mods: test
"""
with salt.utils.files.fopen(reactor_file, "w") as fp:
fp.write(content)
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
ret = comp.render_template(template=str(reactor_file), kwargs=kwargs)
assert ret["highstate_run"]["local"][0]["tgt"] == minion
assert ret["highstate_run"]["local"][1]["args"][0]["mods"] == "test"
def test_compiler_render_template_doesnotexist(minion_opts, tmp_path):
"""
Test Compiler.render_template when
the reactor file does not exist
"""
minion = "poc-minion"
kwargs = {
"tag": f"salt/minion/{minion}/start",
"data": {
"id": minion,
"cmd": "_minion_event",
"pretag": None,
"data": f"Minion {minion} started at Thu Sep 14 07:31:04 2023",
"tag": f"salt/minion/{minion}/start",
"_stamp": "2023-09-14T13:31:05.000316",
},
}
reactor_file = tmp_path / "reactor.sls"
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
mock_pad = MagicMock(return_value=None)
patch_pad = patch.object(comp, "pad_funcs", mock_pad)
with patch_pad:
ret = comp.render_template(template=str(reactor_file), kwargs=kwargs)
assert ret == {}
mock_pad.assert_not_called()
def test_compiler_pad_funcs(minion_opts, tmp_path):
"""
Test Compiler.pad_funcs
"""
high = OrderedDict(
[
(
"highstate_run",
OrderedDict(
[
(
"local.state.apply",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[("args", [OrderedDict([("mods", "test")])])]
),
],
)
]
),
)
]
)
exp = OrderedDict(
[
(
"highstate_run",
OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[("args", [OrderedDict([("mods", "test")])])]
),
"state.apply",
],
)
]
),
)
]
)
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
ret = comp.pad_funcs(high)
assert ret == exp
def test_compiler_pad_funcs_short_sls(minion_opts, tmp_path):
"""
Test Compiler.pad_funcs when using a shorter
sls with no extra arguments
"""
high = OrderedDict([("master_pub", "wheel.key.master_key_str")])
exp = OrderedDict([("master_pub", {"wheel": ["key.master_key_str"]})])
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
ret = comp.pad_funcs(high)
assert ret == exp
@pytest.mark.parametrize(
"high,exp",
[
(
{
"master_pub": {
"wheel": ["key.master_key_str"],
"__sls__": "/srv/reactor/start.sls",
}
},
[],
),
(set(), ["High data is not a dictionary and is invalid"]),
(
{
1234: {
"wheel": ["key.master_key_str"],
"__sls__": "/srv/reactor/start.sls",
}
},
[
"ID '1234' in SLS '/srv/reactor/start.sls' is not formed as a string, but is type int. It may need to be quoted."
],
),
(
{
b"test": {
"wheel": ["key.master_key_str"],
"__sls__": "/srv/reactor/start.sls",
}
},
[
"ID 'b'test'' in SLS '/srv/reactor/start.sls' is not formed as a string, but is type bytes. It may need to be quoted."
],
),
(
{
True: {
"wheel": ["key.master_key_str"],
"__sls__": "/srv/reactor/start.sls",
}
},
[
"ID 'True' in SLS '/srv/reactor/start.sls' is not formed as a string, but is type bool. It may need to be quoted."
],
),
(
{"master_pub": ["wheel", "key.master_key_str"]},
[
"The type master_pub in ['wheel', 'key.master_key_str'] is not formatted as a dictionary"
],
),
(
{
"master_pub": {
"wheel": {"key.master_key_str"},
"__sls__": "/srv/reactor/start.sls",
}
},
[
"State 'master_pub' in SLS '/srv/reactor/start.sls' is not formed as a list"
],
),
(
{
"master_pub": {
"wheel": ["key. master_key_str"],
"__sls__": "/srv/reactor/start.sls",
}
},
[
'The function "key. master_key_str" in state "master_pub" in SLS "/srv/reactor/start.sls" has whitespace, a function with whitespace is not supported, perhaps this is an argument that is missing a ":"'
],
),
(
{
"master_pub": {
"wheel": ["key.master_key_str "],
"__sls__": "/srv/reactor/start.sls",
}
},
[],
),
],
)
def test_compiler_verify_high_short_sls(minion_opts, tmp_path, high, exp):
"""
Test Compiler.verify_high when using
a shorter sls with know extra arguments
"""
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
ret = comp.verify_high(high)
# empty is successful. Means we have no errors
assert ret == exp
@pytest.mark.parametrize(
"high,exp",
[
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test1")]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict(
[
(
"require",
[OrderedDict([("local", "add_test_1")])],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[],
),
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test1")]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict([("require", {"local": "add_test_1"})]),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[
"The require statement in state 'add_test_2' in SLS '/srv/reactor/start.sls' needs to be formed as a list"
],
),
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test1")]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local.cmd.run",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict([("require", {"local": "add_test_1"})]),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[
"The require statement in state 'add_test_2' in SLS '/srv/reactor/start.sls' needs to be formed as a list",
"Too many functions declared in state 'add_test_2' in SLS '/srv/reactor/start.sls'. Please choose one of the following: cmd.run, cmd.run",
],
),
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[("args", ([("cmd", "touch /tmp/test1")]))]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict([("require", ([("local", "add_test_1")]))]),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[
"Requisite declaration ('local', 'add_test_1') in state add_test_2 in SLS /srv/reactor/start.sls is not formed as a single key dictionary"
],
),
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test1")]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict(
[
(
"require",
[
OrderedDict(
[("local", (["add_test_1"]))]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[
'Illegal requisite "[\'add_test_1\']" in SLS "/srv/reactor/start.sls", please check your syntax.\n'
],
),
(
{
"add_test_1": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test1")]
)
],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
"add_test_2": OrderedDict(
[
(
"local",
[
OrderedDict([("tgt", "poc-minion")]),
OrderedDict(
[
(
"args",
[
OrderedDict(
[("cmd", "touch /tmp/test2")]
)
],
)
]
),
OrderedDict(
[
(
"require",
[OrderedDict([("local", "add_test_1")])],
)
]
),
"cmd.run",
],
),
("__sls__", "/srv/reactor/start.sls"),
]
),
},
[],
),
],
)
def test_compiler_verify_high_sls_requisites(minion_opts, tmp_path, high, exp):
"""
Test Compiler.verify_high when using
a sls with requisites
"""
mminion = salt.minion.MasterMinion(minion_opts)
comp = salt.state.Compiler(minion_opts, mminion.rend)
ret = comp.verify_high(high)
# empty is successful. Means we have no errors
assert ret == exp