From 3b8c57e29d0099efc8351ce7b1f36dc955b3cafa Mon Sep 17 00:00:00 2001 From: Daniel Dehennin Date: Thu, 26 Sep 2024 21:49:48 +0200 Subject: [PATCH] Add test for saltclass nested classes expansion --- tests/pytests/unit/pillar/test_saltclass.py | 188 +++++++++++++++++++- 1 file changed, 183 insertions(+), 5 deletions(-) diff --git a/tests/pytests/unit/pillar/test_saltclass.py b/tests/pytests/unit/pillar/test_saltclass.py index b96db78bc49..f4bb0bcbd57 100644 --- a/tests/pytests/unit/pillar/test_saltclass.py +++ b/tests/pytests/unit/pillar/test_saltclass.py @@ -23,6 +23,8 @@ def temp_saltclass_tree(tmp_path, minion_id): nodes_dir.mkdir(parents=True, exist_ok=True) default_dir = classes_dir / "default" default_dir.mkdir(parents=True, exist_ok=True) + users_dir = default_dir / "users" + users_dir.mkdir(parents=True, exist_ok=True) roles_dir = classes_dir / "roles" roles_dir.mkdir(parents=True, exist_ok=True) nginx_subdir = roles_dir / "nginx" @@ -35,6 +37,9 @@ def temp_saltclass_tree(tmp_path, minion_id): - default.motd - default.empty + states: + - default + pillars: default: network: @@ -50,6 +55,13 @@ def temp_saltclass_tree(tmp_path, minion_id): test_list: - a: ${default:network:ntp:srv1} - ${default:network:ntp:srv2} + + global_scalar: from default + test_dict: + a_scalar: from default + a_list: + - element1 + - element2 """ default_init.write_text(test_list) @@ -57,24 +69,91 @@ def temp_saltclass_tree(tmp_path, minion_id): nodes_text = """ environment: base + states: + - minion_node + classes: {% for class in ['default', 'roles.*', 'empty.*'] %} - {{ class }} {% endfor %} + + pillars: + global_scalar: from minion_node + test_dict: + a_scalar: from minion_node """ minion_node_file.write_text(nodes_text) - (default_dir / "users.yml").write_text("test: this is a test") + (users_dir / "init.yml").write_text( + """ + classes: + - default.users.foo + + states: + - users + + pillars: + default: + ntp: + srv1: 192.168.20.10 + + global_scalar: from users + test_dict: + a_scalar: from users + """ + ) + (users_dir / "foo.yml").write_text( + """ + states: + - users.foo + + global_scalar: from users.foo + users_foo_scalar: from users.foo + test_dict: + a_scalar: from users.foo + """ + ) (default_dir / "empty.yml").write_text("test: this is a test") - (default_dir / "motd.yml").write_text("test: this is a test") - (roles_dir / "app.yml").write_text("test: this is a test") - (nginx_subdir / "init.yml").write_text("test: this is a test") + (default_dir / "motd.yml").write_text( + """ + states: + - motd + + pillars: + global_scalar: from motd + """ + ) + (roles_dir / "app.yml").write_text( + """ + states: + - app + + pillars: + global_scalar: from app + """ + ) + (nginx_subdir / "init.yml").write_text( + """ + states: + - nginx + + pillars: + global_scalar: from nginx + nginx_scalar: from nginx + """ + ) return dirname -def test_succeeds(temp_saltclass_tree): +def test_classes_order(temp_saltclass_tree): + """ + Classes must be correctly ordered. + + See: https://github.com/saltstack/salt/issues/58969 + """ expected_ret = [ + "default.users.foo", "default.users", "default.motd", "default.empty", @@ -110,3 +189,102 @@ def test_list_expansion_succeeds(temp_saltclass_tree): pytest.fail(err) # Else give the parsed content result assert expected_ret == parsed_ret + + +def test_node_override_classes_scalars(temp_saltclass_tree): + """ + Scalars pillars defined in a node definition must override the + definition from classes. + """ + expected_ret = "from minion_node" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["global_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_node_override_classes_scalar_in_dict(temp_saltclass_tree): + """ + Scalars defined in `dict` pillars defined in a node definition must override the + same dict definition from classes. + + See: https://github.com/saltstack/salt/issues/63933 + """ + expected_ret = "from minion_node" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["test_dict"]["a_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_node_override_classes_list_in_dict(temp_saltclass_tree): + """ + `list` under a `dict` defined in a node definition must override the + same definition from classes. + + See: https://github.com/saltstack/salt/issues/63933 + """ + expected_ret = {"srv1": "192.168.10.10", "srv2": "192.168.10.20"} + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["default"]["network"]["ntp"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_list_in_dict_no_duplication(temp_saltclass_tree): + """ + `list` under a `dict` in pillar must not be duplicated. + + See: + """ + expected_ret = ["element1", "element2"] + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["test_dict"]["a_list"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret + + +def test_nested_classes_has_pillars(temp_saltclass_tree): + """ + pillars defined in nested classes are present + """ + expected_ret = "from nginx" + fake_args = {"path": str(temp_saltclass_tree)} + fake_pillar = {} + fake_minion_id = "fake_id" + try: + full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) + parsed_ret = full_ret["nginx_scalar"] + # Fail the test if we hit our NoneType error + except TypeError as err: + pytest.fail(err) + # Else give the parsed content result + assert expected_ret == parsed_ret