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 ability for file.symlink to not set ownership on existing links #63106

Merged
merged 3 commits into from
Dec 2, 2022
Merged
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/63093.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ability for file.symlink to not set ownership on existing links
28 changes: 26 additions & 2 deletions salt/states/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,7 @@ def symlink(
win_inheritance=None,
atomic=False,
disallow_copy_and_unlink=False,
inherit_user_and_group=False,
**kwargs
):
"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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"]

Expand Down
35 changes: 33 additions & 2 deletions tests/pytests/unit/states/file/test_symlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,40 @@ def return_val(kwargs):
), 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.gid_to_group": MagicMock(return_value=group),
"file.readlink": mock_target,
"user.info": mock_t,
},
), 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
), patch(
"salt.states.file._get_symlink_ownership", return_value=(user, group)
):
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