Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to ignore duplicate includes #67082

Open
wants to merge 1 commit into
base: 3007.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/67081.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add option to ignore duplicate includes
3 changes: 3 additions & 0 deletions salt/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,8 @@ def _gather_buffer_space():
"jinja_trim_blocks": bool,
# Cache minion ID to file
"minion_id_caching": bool,
# Allow duplicate includes
"allow_duplicate_includes": bool,
# Always generate minion id in lowercase.
"minion_id_lowercase": bool,
# Remove either a single domain (foo.org), or all (True) from a generated minion id.
Expand Down Expand Up @@ -1271,6 +1273,7 @@ def _gather_buffer_space():
"grains_refresh_every": 0,
"minion_id_caching": True,
"minion_id_lowercase": False,
"allow_duplicate_includes": False,
"minion_id_remove_domain": False,
"keysize": 2048,
"transport": "zeromq",
Expand Down
4 changes: 3 additions & 1 deletion salt/renderers/yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def yaml_loader(*args):
yamlloader = yamlloader_old
else:
yamlloader = yamlloader_new
return yamlloader.SaltYamlSafeLoader(*args, dictclass=OrderedDict)
return yamlloader.SaltYamlSafeLoader(
*args, dictclass=OrderedDict, opts=__opts__
)

return yaml_loader

Expand Down
8 changes: 7 additions & 1 deletion salt/utils/yamlloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ class SaltYamlSafeLoader(BaseLoader):
to make things like sls file more intuitive.
"""

def __init__(self, stream, dictclass=dict):
def __init__(self, stream, dictclass=dict, opts=None):
super().__init__(stream)
self.__opts__ = opts or {}
if dictclass is not dict:
# then assume ordered dict and use it for both !map and !omap
self.add_constructor("tag:yaml.org,2002:map", type(self).construct_yaml_map)
Expand Down Expand Up @@ -76,6 +77,11 @@ def construct_mapping(self, node, deep=False):
)
value = self.construct_object(value_node, deep=deep)
if key in mapping:
if key == "include" and self.__opts__.get(
"allow_duplicate_includes", False
):
continue

raise ConstructorError(
context,
node.start_mark,
Expand Down
8 changes: 7 additions & 1 deletion salt/utils/yamlloader_old.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ class SaltYamlSafeLoader(yaml.SafeLoader):
to make things like sls file more intuitive.
"""

def __init__(self, stream, dictclass=dict):
def __init__(self, stream, dictclass=dict, opts=None):
super().__init__(stream)
self.__opts__ = opts or {}
if dictclass is not dict:
# then assume ordered dict and use it for both !map and !omap
self.add_constructor("tag:yaml.org,2002:map", type(self).construct_yaml_map)
Expand Down Expand Up @@ -81,6 +82,11 @@ def construct_mapping(self, node, deep=False):
)
value = self.construct_object(value_node, deep=deep)
if key in mapping:
if key == "include" and self.__opts__.get(
"allow_duplicate_includes", False
):
continue

raise ConstructorError(
context,
node.start_mark,
Expand Down
111 changes: 111 additions & 0 deletions tests/unit/utils/test_yamldumper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Unit tests for salt.utils.yamldumper
"""

import pytest
from yaml.constructor import ConstructorError

import salt.utils.yamldumper
from tests.support.unit import TestCase


class YamlDumperTestCase(TestCase):
"""
TestCase for salt.utils.yamldumper module
"""

def test_yaml_dump(self):
"""
Test yaml.dump a dict
"""
data = {"foo": "bar"}
exp_yaml = "{foo: bar}\n"

assert salt.utils.yamldumper.dump(data) == exp_yaml

assert salt.utils.yamldumper.dump(
data, default_flow_style=False
) == exp_yaml.replace("{", "").replace("}", "")

def test_yaml_safe_dump(self):
"""
Test yaml.safe_dump a dict
"""
data = {"foo": "bar"}
assert salt.utils.yamldumper.safe_dump(data) == "{foo: bar}\n"

assert (
salt.utils.yamldumper.safe_dump(data, default_flow_style=False)
== "foo: bar\n"
)

def test_yaml_loader_with_opts(self):
"""
Test SaltYamlSafeLoader with __opts__
"""
yaml_data = "key: value\n"
opts = {"some_key": "some_value"}
loader = salt.utils.yamlloader.SaltYamlSafeLoader(yaml_data, opts=opts)
data = loader.get_single_data()

# Check that data is parsed correctly
assert data == {"key": "value"}

# Check that __opts__ is accessible
assert loader.__opts__ == opts

def test_construct_mapping_with_opts(self):
"""
Test construct_mapping with __opts__
"""
yaml_data = """
key1: value1
key2: value2
"""
opts = {"custom_behavior": "test_value"}
loader = salt.utils.yamlloader.SaltYamlSafeLoader(yaml_data, opts=opts)
mapping = loader.get_single_data()

# Ensure mapping was constructed correctly
assert mapping == {"key1": "value1", "key2": "value2"}

# Optionally, add logic to test how `__opts__` affects the loader
assert loader.__opts__.get("custom_behavior") == "test_value"

def test_allow_duplicate_includes(self):
"""
Test allow_duplicate_includes=True
"""
yaml_data = """
include:
- foo
- bar
include:
- foo
- bar
"""
opts = {"allow_duplicate_includes": True}
loader = salt.utils.yamlloader.SaltYamlSafeLoader(yaml_data, opts=opts)
data = loader.get_single_data()

assert data == {
"include": ["foo", "bar"],
}

def test_allow_duplicate_includes_false(self):
"""
Test allow_duplicate_includes=False
"""
yaml_data = """
include:
- foo
- bar
include:
- foo
- bar
"""
opts = {"allow_duplicate_includes": False}

with pytest.raises(ConstructorError, match="found conflicting ID 'include'"):
loader = salt.utils.yamlloader.SaltYamlSafeLoader(yaml_data, opts=opts)
loader.get_single_data()
Loading