From edf6e15c2fd9767f13d0fd52f140fa2a41e994da Mon Sep 17 00:00:00 2001 From: Dafydd Jones Date: Wed, 3 Dec 2025 18:13:51 +0000 Subject: [PATCH 1/2] Adds `alias` state module back in to core This was removed as part of the community module migration, but provides key OS functionality, and the associated execution module was not removed. --- doc/ref/states/all/index.rst | 1 + doc/ref/states/all/salt.states.alias.rst | 5 + salt/states/alias.py | 87 ++++++++++++ tests/pytests/unit/states/test_alias.py | 167 +++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 doc/ref/states/all/salt.states.alias.rst create mode 100644 salt/states/alias.py create mode 100644 tests/pytests/unit/states/test_alias.py diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index 2cee447109c5..ed9db824bdb2 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -10,6 +10,7 @@ state modules :toctree: :template: autosummary.rst.tmpl + alias ansiblegate apache apache_conf diff --git a/doc/ref/states/all/salt.states.alias.rst b/doc/ref/states/all/salt.states.alias.rst new file mode 100644 index 000000000000..e66857609ab0 --- /dev/null +++ b/doc/ref/states/all/salt.states.alias.rst @@ -0,0 +1,5 @@ +salt.states.alias +================= + +.. automodule:: salt.states.alias + :members: diff --git a/salt/states/alias.py b/salt/states/alias.py new file mode 100644 index 000000000000..48b153b5f6e2 --- /dev/null +++ b/salt/states/alias.py @@ -0,0 +1,87 @@ +""" +Configuration of email aliases + +The mail aliases file can be managed to contain definitions for specific email +aliases: + +.. code-block:: yaml + + username: + alias.present: + - target: user@example.com + +.. code-block:: yaml + + thomas: + alias.present: + - target: thomas@example.com + +The default alias file is set to ``/etc/aliases``, as defined in Salt's +:mod:`config execution module `. To change the alias +file from the default location, set the following in your minion config: + +.. code-block:: yaml + + aliases.file: /my/alias/file + +""" + + +def present(name, target): + """ + Ensures that the named alias is present with the given target or list of + targets. If the alias exists but the target differs from the previous + entry, the target(s) will be overwritten. If the alias does not exist, the + alias will be created. + + name + The local user/address to assign an alias to + + target + The forwarding address + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if __salt__["aliases.has_target"](name, target): + ret["result"] = True + ret["comment"] = f"Alias {name} already present" + return ret + if __opts__["test"]: + ret["result"] = None + ret["comment"] = f"Alias {name} -> {target} is set to be added" + return ret + if __salt__["aliases.set_target"](name, target): + ret["changes"] = {"alias": name} + ret["result"] = True + ret["comment"] = f"Set email alias {name} -> {target}" + return ret + else: + ret["result"] = False + ret["comment"] = f"Failed to set alias {name} -> {target}" + return ret + + +def absent(name): + """ + Ensure that the named alias is absent + + name + The alias to remove + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if not __salt__["aliases.get_target"](name): + ret["result"] = True + ret["comment"] = f"Alias {name} already absent" + return ret + if __opts__["test"]: + ret["result"] = None + ret["comment"] = f"Alias {name} is set to be removed" + return ret + if __salt__["aliases.rm_alias"](name): + ret["changes"] = {"alias": name} + ret["result"] = True + ret["comment"] = f"Removed alias {name}" + return ret + else: + ret["result"] = False + ret["comment"] = f"Failed to remove alias {name}" + return ret diff --git a/tests/pytests/unit/states/test_alias.py b/tests/pytests/unit/states/test_alias.py new file mode 100644 index 000000000000..c9e4473b230a --- /dev/null +++ b/tests/pytests/unit/states/test_alias.py @@ -0,0 +1,167 @@ +""" +unit tests for the alias state +""" + +import pytest + +import salt.states.alias as alias +from tests.support.mock import MagicMock, patch + + +@pytest.fixture +def configure_loader_modules(): + return {alias: {}} + + +def test_present_has_target(): + """ + test alias.present has target already + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": f"Alias {name} already present", + "changes": {}, + "name": name, + "result": True, + } + + has_target = MagicMock(return_value=True) + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + assert alias.present(name, target) == ret + + +def test_present_has_not_target_test(): + """ + test alias.present has not target yet test mode + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": f"Alias {name} -> {target} is set to be added", + "changes": {}, + "name": name, + "result": None, + } + + has_target = MagicMock(return_value=False) + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": True}): + assert alias.present(name, target) == ret + + +def test_present_set_target(): + """ + test alias.present set target + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": f"Set email alias {name} -> {target}", + "changes": {"alias": name}, + "name": name, + "result": True, + } + + has_target = MagicMock(return_value=False) + set_target = MagicMock(return_value=True) + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.set_target": set_target}): + assert alias.present(name, target) == ret + + +def test_present_set_target_failed(): + """ + test alias.present set target failure + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": f"Failed to set alias {name} -> {target}", + "changes": {}, + "name": name, + "result": False, + } + + has_target = MagicMock(return_value=False) + set_target = MagicMock(return_value=False) + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.set_target": set_target}): + assert alias.present(name, target) == ret + + +def test_absent_already_gone(): + """ + test alias.absent already gone + """ + name = "saltdude" + ret = { + "comment": f"Alias {name} already absent", + "changes": {}, + "name": name, + "result": True, + } + + get_target = MagicMock(return_value=False) + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + assert alias.absent(name) == ret + + +def test_absent_not_gone_test(): + """ + test alias.absent already gone test mode + """ + name = "saltdude" + ret = { + "comment": f"Alias {name} is set to be removed", + "changes": {}, + "name": name, + "result": None, + } + + get_target = MagicMock(return_value=True) + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": True}): + assert alias.absent(name) == ret + + +def test_absent_rm_alias(): + """ + test alias.absent remove alias + """ + name = "saltdude" + ret = { + "comment": f"Removed alias {name}", + "changes": {"alias": name}, + "name": name, + "result": True, + } + + get_target = MagicMock(return_value=True) + rm_alias = MagicMock(return_value=True) + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.rm_alias": rm_alias}): + assert alias.absent(name) == ret + + +def test_absent_rm_alias_failed(): + """ + test alias.absent remove alias failure + """ + name = "saltdude" + ret = { + "comment": f"Failed to remove alias {name}", + "changes": {}, + "name": name, + "result": False, + } + + get_target = MagicMock(return_value=True) + rm_alias = MagicMock(return_value=False) + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.rm_alias": rm_alias}): + assert alias.absent(name) == ret From 60a35d0e251089768cf36fb9732dfebab3fa25c6 Mon Sep 17 00:00:00 2001 From: Dafydd Jones Date: Fri, 2 Jan 2026 15:56:49 +0000 Subject: [PATCH 2/2] [alias] Add changelog --- changelog/68574.fixed.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/68574.fixed.md diff --git a/changelog/68574.fixed.md b/changelog/68574.fixed.md new file mode 100644 index 000000000000..21941f911f12 --- /dev/null +++ b/changelog/68574.fixed.md @@ -0,0 +1,5 @@ +Adds `alias` state module back in to core. + +Restores the module that had been removed as part of the +community module migration. The associated execution module +had not been migrated.