# Buggy code
def symlink(
    name,
    target,
    force=False,
    backupname=None,
    makedirs=False,
    user=None,
    group=None,
    mode=None,
    **kwargs,
):
    """
    Create a symlink

    If the file already exists and is a symlink pointing to any location other
    than the specified target, the symlink will be replaced. If the symlink is
    a regular file or directory then the state will return False. If the
    regular file or directory is desired to be replaced with a symlink pass
    force: True, if it is to be renamed, pass a backupname.

    name
        The location of the symlink to create

    target
        The location that the symlink points to

    force
        If the name of the symlink exists and is not a symlink and
        force is set to False, the state will fail. If force is set to
        True, the file or directory in the way of the symlink file
        will be deleted to make room for the symlink, unless
        backupname is set, when it will be renamed

    backupname
        If the name of the symlink exists and is not a symlink, it will be
        renamed to the backupname. If the backupname already
        exists and force is False, the state will fail. Otherwise, the
        backupname will be removed first.

    makedirs
        If the location of the symlink does not already have a parent directory
        then the state will fail, setting makedirs to True will allow Salt to
        create the parent directory

    user
        The user to own the file, this defaults to the user salt is running as
        on the minion

    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

    mode
        The permissions to set on this file, aka 644, 0775, 4664. Not supported
        on Windows
    """
    # Make sure that leading zeros stripped by YAML loader are added back
    mode = __salt__["config.manage_mode"](mode)

    user = _test_owner(kwargs, user=user)
    ret = {"name": name, "changes": {}, "result": True, "comment": ""}

    if user is None:
        user = __opts__["user"]

    if salt.utils.is_windows():
        if group is not None:
            log.warning(
                "The group argument for {0} has been ignored as this "
                "is a Windows system.".format(name)
            )
        group = user

    if group is None:
        group = __salt__["file.gid_to_group"](__salt__["user.info"](user).get("gid", 0))

    preflight_errors = []
    uid = __salt__["file.user_to_uid"](user)
    gid = __salt__["file.group_to_gid"](group)

    if uid == "":
        preflight_errors.append("User {0} does not exist".format(user))

    if gid == "":
        preflight_errors.append("Group {0} does not exist".format(group))

    if not os.path.isabs(name):
        preflight_errors.append(
            "Specified file {0} is not an absolute path".format(name)
        )

    if preflight_errors:
        msg = ". ".join(preflight_errors)
        if len(preflight_errors) > 1:
            msg += "."
        return _error(ret, msg)

    if __opts__["test"]:
        ret["result"], ret["comment"] = _symlink_check(name, target, force, user, group)
        return ret

    if not os.path.isdir(os.path.dirname(name)):
        if makedirs:
            __salt__["file.makedirs"](name, user=user, group=group, mode=mode)
        else:
            return _error(
                ret,
                "Directory {0} for symlink is not present".format(
                    os.path.dirname(name)
                ),
            )
    if __salt__["file.is_link"](name):
        # The link exists, verify that it matches the target
        if __salt__["file.readlink"](name) != target:
            # The target is wrong, delete the link
            os.remove(name)
        else:
            if _check_symlink_ownership(name, user, group):
                # The link looks good!
                ret["comment"] = "Symlink {0} is present and owned by {1}:{2}".format(
                    name, user, group
                )
            else:
                if _set_symlink_ownership(name, user, group):
                    ret["comment"] = "Set ownership of symlink {0} to {1}:{2}".format(
                        name, user, group
                    )
                    ret["changes"]["ownership"] = "{0}:{1}".format(user, group)
                else:
                    ret["result"] = False
                    ret["comment"] += (
                        "Failed to set ownership of symlink {0} to {1}:{2}".format(
                            name, user, group
                        )
                    )
            return ret

    elif os.path.isfile(name) or os.path.isdir(name):
        # It is not a link, but a file or dir
        if backupname is not None:
            # Make a backup first
            if os.path.lexists(backupname):
                if not force:
                    return _error(
                        ret,
                        (
                            (
                                "File exists where the backup target {0} should go"
                            ).format(backupname)
                        ),
                    )
                elif os.path.isfile(backupname):
                    os.remove(backupname)
                elif os.path.isdir(backupname):
                    shutil.rmtree(backupname)
                else:
                    return _error(
                        ret,
                        (
                            (
                                "Something exists where the backup target {0}should go"
                            ).format(backupname)
                        ),
                    )
            os.rename(name, backupname)
        elif force:
            # Remove whatever is in the way
            if os.path.isfile(name):
                os.remove(name)
                ret["changes"]["forced"] = "Symlink was forcibly replaced"
            else:
                shutil.rmtree(name)
        else:
            # Otherwise throw an error
            if os.path.isfile(name):
                return _error(
                    ret, ("File exists where the symlink {0} should be".format(name))
                )
            else:
                return _error(
                    ret,
                    (("Directory exists where the symlink {0} should be").format(name)),
                )

    if not os.path.exists(name):
        # The link is not present, make it
        try:
            __salt__["file.symlink"](target, name)
        except OSError as exc:
            ret["result"] = False
            ret["comment"] = "Unable to create new symlink {0} -> {1}: {2}".format(
                name, target, exc
            )
            return ret
        else:
            ret["comment"] = "Created new symlink {0} -> {1}".format(name, target)
            ret["changes"]["new"] = name

        if not _check_symlink_ownership(name, user, group):
            if not _set_symlink_ownership(name, user, group):
                ret["result"] = False
                ret["comment"] += ", but was unable to set ownership to {0}:{1}".format(
                    user, group
                )
    return ret


# Traceback (TypeError)
2014-11-24 16:03:42,245 [salt.minion      ][WARNING ] TypeError encountered executing state.highstate: expected string or buffer. See debug log for more info.  Possibly a missing arguments issue:  ArgSpec(args=['test', 'queue'], varargs=None, keywords='kwargs', defaults=(None, False))
Traceback (most recent call last):
File "/usr/lib/pymodules/python2.7/salt/minion.py", line 809, in _thread_return
return_data = func(*args, **kwargs)
File "/usr/lib/pymodules/python2.7/salt/modules/state.py", line 275, in highstate
force=kwargs.get('force', False)
File "/usr/lib/pymodules/python2.7/salt/state.py", line 2546, in call_highstate
return self.state.call_high(high)
File "/usr/lib/pymodules/python2.7/salt/state.py", line 1715, in call_high
ret = self.call_chunks(chunks)
File "/usr/lib/pymodules/python2.7/salt/state.py", line 1437, in call_chunks
running = self.call_chunk(low, running, chunks)
File "/usr/lib/pymodules/python2.7/salt/state.py", line 1562, in call_chunk
status = self.check_requisite(low, running, chunks, True)
File "/usr/lib/pymodules/python2.7/salt/state.py", line 1489, in check_requisite
if (fnmatch.fnmatch(chunk['name'], req_val) or
File "/usr/lib/python2.7/fnmatch.py", line 43, in fnmatch
return fnmatchcase(name, pat)
File "/usr/lib/python2.7/fnmatch.py", line 79, in fnmatchcase
return _cache[pat].match(name) is not None
TypeError: expected string or buffer

# Fixed code
def symlink(
    name,
    target,
    force=False,
    backupname=None,
    makedirs=False,
    user=None,
    group=None,
    mode=None,
    **kwargs,
):
    """
    Create a symlink

    If the file already exists and is a symlink pointing to any location other
    than the specified target, the symlink will be replaced. If the symlink is
    a regular file or directory then the state will return False. If the
    regular file or directory is desired to be replaced with a symlink pass
    force: True, if it is to be renamed, pass a backupname.

    name
        The location of the symlink to create

    target
        The location that the symlink points to

    force
        If the name of the symlink exists and is not a symlink and
        force is set to False, the state will fail. If force is set to
        True, the file or directory in the way of the symlink file
        will be deleted to make room for the symlink, unless
        backupname is set, when it will be renamed

    backupname
        If the name of the symlink exists and is not a symlink, it will be
        renamed to the backupname. If the backupname already
        exists and force is False, the state will fail. Otherwise, the
        backupname will be removed first.

    makedirs
        If the location of the symlink does not already have a parent directory
        then the state will fail, setting makedirs to True will allow Salt to
        create the parent directory

    user
        The user to own the file, this defaults to the user salt is running as
        on the minion

    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

    mode
        The permissions to set on this file, aka 644, 0775, 4664. Not supported
        on Windows
    """
    # Make sure that leading zeros stripped by YAML loader are added back
    mode = __salt__["config.manage_mode"](mode)

    user = _test_owner(kwargs, user=user)
    ret = {"name": name, "changes": {}, "result": True, "comment": ""}
    if not name:
        return _error(ret, "Must provide name to file.symlink")

    if user is None:
        user = __opts__["user"]

    if salt.utils.is_windows():
        if group is not None:
            log.warning(
                "The group argument for {0} has been ignored as this "
                "is a Windows system.".format(name)
            )
        group = user

    if group is None:
        group = __salt__["file.gid_to_group"](__salt__["user.info"](user).get("gid", 0))

    preflight_errors = []
    uid = __salt__["file.user_to_uid"](user)
    gid = __salt__["file.group_to_gid"](group)

    if uid == "":
        preflight_errors.append("User {0} does not exist".format(user))

    if gid == "":
        preflight_errors.append("Group {0} does not exist".format(group))

    if not os.path.isabs(name):
        preflight_errors.append(
            "Specified file {0} is not an absolute path".format(name)
        )

    if preflight_errors:
        msg = ". ".join(preflight_errors)
        if len(preflight_errors) > 1:
            msg += "."
        return _error(ret, msg)

    if __opts__["test"]:
        ret["result"], ret["comment"] = _symlink_check(name, target, force, user, group)
        return ret

    if not os.path.isdir(os.path.dirname(name)):
        if makedirs:
            __salt__["file.makedirs"](name, user=user, group=group, mode=mode)
        else:
            return _error(
                ret,
                "Directory {0} for symlink is not present".format(
                    os.path.dirname(name)
                ),
            )
    if __salt__["file.is_link"](name):
        # The link exists, verify that it matches the target
        if __salt__["file.readlink"](name) != target:
            # The target is wrong, delete the link
            os.remove(name)
        else:
            if _check_symlink_ownership(name, user, group):
                # The link looks good!
                ret["comment"] = "Symlink {0} is present and owned by {1}:{2}".format(
                    name, user, group
                )
            else:
                if _set_symlink_ownership(name, user, group):
                    ret["comment"] = "Set ownership of symlink {0} to {1}:{2}".format(
                        name, user, group
                    )
                    ret["changes"]["ownership"] = "{0}:{1}".format(user, group)
                else:
                    ret["result"] = False
                    ret["comment"] += (
                        "Failed to set ownership of symlink {0} to {1}:{2}".format(
                            name, user, group
                        )
                    )
            return ret

    elif os.path.isfile(name) or os.path.isdir(name):
        # It is not a link, but a file or dir
        if backupname is not None:
            # Make a backup first
            if os.path.lexists(backupname):
                if not force:
                    return _error(
                        ret,
                        (
                            (
                                "File exists where the backup target {0} should go"
                            ).format(backupname)
                        ),
                    )
                elif os.path.isfile(backupname):
                    os.remove(backupname)
                elif os.path.isdir(backupname):
                    shutil.rmtree(backupname)
                else:
                    return _error(
                        ret,
                        (
                            (
                                "Something exists where the backup target {0}should go"
                            ).format(backupname)
                        ),
                    )
            os.rename(name, backupname)
        elif force:
            # Remove whatever is in the way
            if os.path.isfile(name):
                os.remove(name)
                ret["changes"]["forced"] = "Symlink was forcibly replaced"
            else:
                shutil.rmtree(name)
        else:
            # Otherwise throw an error
            if os.path.isfile(name):
                return _error(
                    ret, ("File exists where the symlink {0} should be".format(name))
                )
            else:
                return _error(
                    ret,
                    (("Directory exists where the symlink {0} should be").format(name)),
                )

    if not os.path.exists(name):
        # The link is not present, make it
        try:
            __salt__["file.symlink"](target, name)
        except OSError as exc:
            ret["result"] = False
            ret["comment"] = "Unable to create new symlink {0} -> {1}: {2}".format(
                name, target, exc
            )
            return ret
        else:
            ret["comment"] = "Created new symlink {0} -> {1}".format(name, target)
            ret["changes"]["new"] = name

        if not _check_symlink_ownership(name, user, group):
            if not _set_symlink_ownership(name, user, group):
                ret["result"] = False
                ret["comment"] += ", but was unable to set ownership to {0}:{1}".format(
                    user, group
                )
    return ret