diff --git a/changelog/63093.added b/changelog/63093.added new file mode 100644 index 00000000000..1a401125088 --- /dev/null +++ b/changelog/63093.added @@ -0,0 +1 @@ +Add ability for file.symlink to not set ownership on existing links diff --git a/salt/states/file.py b/salt/states/file.py index 3f349bff844..e76a7cdd97e 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -1537,6 +1537,7 @@ def symlink( win_inheritance=None, atomic=False, disallow_copy_and_unlink=False, + inherit_user_and_group=False, **kwargs ): """ @@ -1581,11 +1582,13 @@ def symlink( user The user to own the file, this defaults to the user salt is running as - on the minion + on the minion unless the link already exists and + ``inherit_user_and_group`` is set group The group ownership set for the file, this defaults to the group salt - is running as on the minion. On Windows, this is ignored + is running as on the minion unless the link already exists and + ``inherit_user_and_group`` is set. On Windows, this is ignored mode The permissions to set on this file, aka 644, 0775, 4664. Not supported @@ -1630,6 +1633,15 @@ def symlink( ``shutil.move`` will be used in order to fall back on a "copy then unlink" approach, which is required for moving across filesystems. + .. versionadded:: 3006.0 + + inherit_user_and_group + If set to ``True``, the link already exists, and either ``user`` or + ``group`` are not set, this parameter will inform Salt to pull the user + and group information from the existing link and use it where ``user`` + or ``group`` is not set. The ``user`` and ``group`` parameters will + override this behavior. + .. versionadded:: 3006.0 """ name = os.path.expanduser(name) @@ -1642,6 +1654,18 @@ def symlink( mode = salt.utils.files.normalize_mode(mode) user = _test_owner(kwargs, user=user) + + if ( + inherit_user_and_group + and (user is None or group is None) + and __salt__["file.is_link"](name) + ): + cur_user, cur_group = _get_symlink_ownership(name) + if user is None: + user = cur_user + if group is None: + group = cur_group + if user is None: user = __opts__["user"] diff --git a/tests/pytests/unit/states/file/test_symlink.py b/tests/pytests/unit/states/file/test_symlink.py index 384772937c4..0551ce5559b 100644 --- a/tests/pytests/unit/states/file/test_symlink.py +++ b/tests/pytests/unit/states/file/test_symlink.py @@ -392,9 +392,36 @@ def test_symlink(): ), patch( "salt.states.file._check_symlink_ownership", return_value=True ): - group = None - comt = "Created new symlink {} -> {}".format(name, target) ret = return_val({"comment": comt, "result": True, "changes": {"new": name}}) res = filestate.symlink(name, target, user=user, group=user) assert res == ret + + with patch.dict( + filestate.__salt__, + { + "file.is_link": mock_t, + "file.get_user": mock_user, + "file.get_group": mock_grp, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.readlink": mock_target, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", MagicMock(side_effect=[True, False]) + ), patch.object( + os.path, "isfile", mock_f + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ), patch( + "salt.states.file._set_symlink_ownership", return_value=True + ), patch( + "salt.states.file._check_symlink_ownership", return_value=True + ): + if salt.utils.platform.is_windows(): + comt = "Symlink {} is present and owned by {}".format(name, user) + else: + comt = "Symlink {} is present and owned by {}:{}".format(name, user, group) + ret = return_val({"comment": comt, "result": True, "changes": {}}) + res = filestate.symlink(name, target, inherit_user_and_group=True) + assert res == ret