Skip to content
Draft
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
2 changes: 2 additions & 0 deletions changelog/68673.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Remove ini sections not present when ``strict: True`` in ``ini.options_present``.

23 changes: 23 additions & 0 deletions doc/ref/states/all/salt.states.ini_manage.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
salt.states.ini_manage
======================

Strict mode behavior
--------------------

When ``strict: True`` is used with ``ini.options_present``, the ini file is
pruned to match the ``sections`` dictionary:

- keys not listed at the top level are removed
- keys not listed inside a section are removed
- sections not listed are removed entirely

Example:

.. code-block:: yaml

/etc/app/config.ini:
ini.options_present:
- strict: True
- sections:
service:
enabled: true
network:
port: 1234

.. automodule:: salt.states.ini_manage
:members:
27 changes: 27 additions & 0 deletions salt/modules/ini_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,33 @@ def remove_section(file_name, section, separator="=", encoding=None):
return ret


def remove_sections(file_name, sections, separator="=", encoding=None):
"""
Remove multiple sections from an ini file. Returns a dictionary of
removed sections with their values.

Args:

file_name (str):
The full path to the ini file.

sections (list):
A list of section names to remove.

Returns:
dict: Mapping of section name to removed section values (or None).
"""
removed = {}
for section in sections or []:
removed[section] = remove_section(
file_name=file_name,
section=section,
separator=separator,
encoding=encoding,
)
return removed


def get_ini(file_name, separator="=", encoding=None):
"""
Retrieve the whole structure from an ini file and return it as a dictionary.
Expand Down
29 changes: 29 additions & 0 deletions salt/states/ini_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,35 @@ def options_present(
}
}
)
desired_sections = {
sname
for sname, sbody in sections.items()
if isinstance(sbody, (dict, OrderedDict))
}
sections_to_remove = set(original_sections).difference(desired_sections)
if sections_to_remove:
if __opts__["test"]:
for section_name in sorted(sections_to_remove):
ret["comment"] += (
f"Removed section {section_name}.\n"
)
ret["result"] = None
else:
removed_sections = __salt__["ini.remove_sections"](
file_name=name,
sections=sorted(sections_to_remove),
separator=separator,
encoding=encoding,
)
for section_name in sections_to_remove:
changes.update(
{
section_name: {
"before": removed_sections.get(section_name),
"after": None,
}
}
)
for section_name, section_body in [
(sname, sbody)
for sname, sbody in sections.items()
Expand Down
26 changes: 26 additions & 0 deletions tests/pytests/unit/states/test_ini_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def configure_loader_modules():
"__salt__": {
"ini.get_ini": mod_ini_manage.get_ini,
"ini.set_option": mod_ini_manage.set_option,
"ini.remove_option": mod_ini_manage.remove_option,
"ini.remove_sections": mod_ini_manage.remove_sections,
},
"__opts__": {"test": False},
},
Expand Down Expand Up @@ -110,6 +112,30 @@ def test_options_present_true_file(tmp_path, sections):
assert mod_ini_manage.get_ini(name) == sections


def test_options_present_strict_removes_sections(tmp_path, sections):
"""
Test to verify strict options_present removes sections not in the input.
"""
name = str(tmp_path / "test_strict_remove_section.ini")

initial = copy.deepcopy(sections)
initial["extra"] = OrderedDict()
initial["extra"]["enabled"] = "true"
initial["other"] = OrderedDict()
initial["other"]["value"] = "42"
ini_manage.options_present(name, initial)

ret = ini_manage.options_present(name, sections, strict=True)

assert ret["result"] is True
assert ret["comment"] == "Changes take effect"
assert ret["changes"] == {
"extra": {"before": {"enabled": "true"}, "after": None},
"other": {"before": {"value": "42"}, "after": None},
}
assert mod_ini_manage.get_ini(name) == sections


def test_options_absent():
"""
Test to verify options absent in file.
Expand Down