.. versionadded:: 2014.7.0
    .. versionchanged:: 2018.3.0

        Added support for the ``pkgin`` provider.

    Verify that the system is completely up to date.

    name
        The name has no functional value and is only used as a tracking
        reference

    refresh
        refresh the package database before checking for new upgrades

    pkgs
        list of packages to upgrade

    :param str cache_valid_time:
        This parameter sets the value in seconds after which cache marked as invalid,
        and cache update is necessary. This overwrite ``refresh`` parameter
        default behavior.

        In this case cache_valid_time is set, refresh will not take place for
        amount in seconds since last ``apt-get update`` executed on the system.

        .. note::

            This parameter available only on Debian based distributions, and
            have no effect on the rest.

    :param bool resolve_capabilities:
        Turn on resolving capabilities. This allow one to name "provides" or alias names for packages.

        .. versionadded:: 2018.3.0

    kwargs
        Any keyword arguments to pass through to ``pkg.upgrade``.

        .. versionadded:: 2015.5.0

def uptodate(name, refresh=False, pkgs=None, **kwargs):
    '''
    .. versionadded:: 2014.7.0
    .. versionchanged:: 2018.3.0

        Added support for the ``pkgin`` provider.

    Verify that the system is completely up to date.

    name
        The name has no functional value and is only used as a tracking
        reference

    refresh
        refresh the package database before checking for new upgrades

    pkgs
        list of packages to upgrade

    :param str cache_valid_time:
        This parameter sets the value in seconds after which cache marked as invalid,
        and cache update is necessary. This overwrite ``refresh`` parameter
        default behavior.

        In this case cache_valid_time is set, refresh will not take place for
        amount in seconds since last ``apt-get update`` executed on the system.

        .. note::

            This parameter available only on Debian based distributions, and
            have no effect on the rest.

    :param bool resolve_capabilities:
        Turn on resolving capabilities. This allow one to name "provides" or alias names for packages.

        .. versionadded:: 2018.3.0

    kwargs
        Any keyword arguments to pass through to ``pkg.upgrade``.

        .. versionadded:: 2015.5.0
    '''
    ret = {'name': name,
           'changes': {},
           'result': False,
           'comment': 'Failed to update'}

    if 'pkg.list_upgrades' not in __salt__:
        ret['comment'] = 'State pkg.uptodate is not available'
        return ret

    # emerge --update doesn't appear to support repo notation
    if 'fromrepo' in kwargs and __grains__['os_family'] == 'Gentoo':
        ret['comment'] = '\'fromrepo\' argument not supported on this platform'
        return ret

    if isinstance(refresh, bool):
        pkgs, refresh = _resolve_capabilities(pkgs, refresh=refresh, **kwargs)
        try:
            packages = __salt__['pkg.list_upgrades'](refresh=refresh, **kwargs)
            expected = {pkgname: {'new': pkgver, 'old': __salt__['pkg.version'](pkgname, **kwargs)}
                        for pkgname, pkgver in six.iteritems(packages)}
            if isinstance(pkgs, list):
                packages = [pkg for pkg in packages if pkg in pkgs]
                expected = {pkgname: pkgver for pkgname, pkgver in six.iteritems(expected) if pkgname in pkgs}
        except Exception as exc:
            ret['comment'] = six.text_type(exc)
            return ret
    else:
        ret['comment'] = 'refresh must be either True or False'
        return ret

    if not packages:
        ret['comment'] = 'System is already up-to-date'
        ret['result'] = True
        return ret
    elif __opts__['test']:
        ret['comment'] = 'System update will be performed'
        ret['changes'] = expected
        ret['result'] = None
        return ret

    try:
        ret['changes'] = __salt__['pkg.upgrade'](refresh=refresh, pkgs=pkgs, **kwargs)
    except CommandExecutionError as exc:
        if exc.info:
            # Get information for state return from the exception.
            ret['changes'] = exc.info.get('changes', {})
            ret['comment'] = exc.strerror_without_changes
        else:
            ret['changes'] = {}
            ret['comment'] = ('An error was encountered while updating '
                              'packages: {0}'.format(exc))
        return ret

    # If a package list was provided, ensure those packages were updated
    missing = []
    if isinstance(pkgs, list):
        missing = [pkg for pkg in six.iterkeys(expected) if pkg not in ret['changes']]

    if missing:
        ret['comment'] = 'The following package(s) failed to update: {0}'.format(', '.join(missing))
        ret['result'] = False
    else:
        ret['comment'] = 'Upgrade ran successfully'
        ret['result'] = True

    return ret