Merge pull request #45991 from terminalmage/fix-duplicate-extra-opts

yumpkg: Fix a couple issues with _get_extra_opts
This commit is contained in:
Nicole Thomas 2018-02-14 11:48:28 -05:00 committed by GitHub
commit 1279924f5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 486 additions and 71 deletions

View File

@ -204,25 +204,29 @@ def _check_versionlock():
)
def _get_repo_options(**kwargs):
def _get_options(**kwargs):
'''
Returns a list of '--enablerepo' and '--disablerepo' options to be used
in the yum command, based on the kwargs.
Returns a list of options to be used in the yum/dnf command, based on the
kwargs passed.
'''
# Get repo options from the kwargs
fromrepo = kwargs.pop('fromrepo', '')
repo = kwargs.pop('repo', '')
disablerepo = kwargs.pop('disablerepo', '')
enablerepo = kwargs.pop('enablerepo', '')
disableexcludes = kwargs.pop('disableexcludes', '')
branch = kwargs.pop('branch', '')
get_extra_options = kwargs.pop('get_extra_options', False)
# Support old 'repo' argument
if repo and not fromrepo:
fromrepo = repo
ret = []
if fromrepo:
log.info('Restricting to repo \'%s\'', fromrepo)
ret.extend(['--disablerepo=*', '--enablerepo=' + fromrepo])
ret.extend(['--disablerepo=*', '--enablerepo={0}'.format(fromrepo)])
else:
if disablerepo:
targets = [disablerepo] \
@ -238,46 +242,30 @@ def _get_repo_options(**kwargs):
else enablerepo
log.info('Enabling repo(s): %s', ', '.join(targets))
ret.extend(['--enablerepo={0}'.format(x) for x in targets])
return ret
if disableexcludes:
log.info('Disabling excludes for \'%s\'', disableexcludes)
ret.append('--disableexcludes={0}'.format(disableexcludes))
def _get_excludes_option(**kwargs):
'''
Returns a list of '--disableexcludes' option to be used in the yum command,
based on the kwargs.
'''
disable_excludes = kwargs.pop('disableexcludes', '')
ret = []
if disable_excludes:
log.info('Disabling excludes for \'%s\'', disable_excludes)
ret.append('--disableexcludes={0}'.format(disable_excludes))
return ret
def _get_branch_option(**kwargs):
'''
Returns a list of '--branch' option to be used in the yum command,
based on the kwargs. This feature requires 'branch' plugin for YUM.
'''
branch = kwargs.pop('branch', '')
ret = []
if branch:
log.info('Adding branch \'%s\'', branch)
ret.append('--branch=\'{0}\''.format(branch))
return ret
ret.append('--branch={0}'.format(branch))
def _get_extra_options(**kwargs):
'''
Returns list of extra options for yum
'''
ret = []
kwargs = salt.utils.clean_kwargs(**kwargs)
for key, value in six.iteritems(kwargs):
if get_extra_options:
# sorting here to make order uniform, makes unit testing more reliable
for key in sorted(kwargs):
if key.startswith('__'):
continue
value = kwargs[key]
if isinstance(value, six.string_types):
ret.append('--{0}=\'{1}\''.format(key, value))
log.info('Found extra option --%s=%s', key, value)
ret.append('--{0}={1}'.format(key, value))
elif value is True:
log.info('Found extra option --%s', key)
ret.append('--{0}'.format(key))
if ret:
log.info('Adding extra options: %s', ret)
return ret
@ -441,8 +429,7 @@ def latest_version(*names, **kwargs):
if len(names) == 0:
return ''
repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs)
options = _get_options(**kwargs)
# Refresh before looking for the latest version available
if refresh:
@ -452,8 +439,7 @@ def latest_version(*names, **kwargs):
# Get available versions for specified package(s)
cmd = [_yum(), '--quiet']
cmd.extend(repo_arg)
cmd.extend(exclude_arg)
cmd.extend(options)
cmd.extend(['list', 'available'])
cmd.extend(names)
out = __salt__['cmd.run_all'](cmd,
@ -761,7 +747,7 @@ def list_repo_pkgs(*args, **kwargs):
disablerepo = kwargs.pop('disablerepo', '') or ''
enablerepo = kwargs.pop('enablerepo', '') or ''
repo_arg = _get_repo_options(fromrepo=fromrepo, **kwargs)
repo_arg = _get_options(fromrepo=fromrepo, **kwargs)
if fromrepo and not isinstance(fromrepo, list):
try:
@ -913,15 +899,13 @@ def list_upgrades(refresh=True, **kwargs):
salt '*' pkg.list_upgrades
'''
repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs)
options = _get_options(**kwargs)
if salt.utils.is_true(refresh):
refresh_db(check_update=False, **kwargs)
cmd = [_yum(), '--quiet']
cmd.extend(repo_arg)
cmd.extend(exclude_arg)
cmd.extend(options)
cmd.extend(['list', 'upgrades' if _yum() == 'dnf' else 'updates'])
out = __salt__['cmd.run_all'](cmd,
output_loglevel='trace',
@ -1039,21 +1023,19 @@ def refresh_db(**kwargs):
check_update_ = kwargs.pop('check_update', True)
repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs)
branch_arg = _get_branch_option(**kwargs)
options = _get_options(**kwargs)
clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache']
update_cmd = [_yum(), '--quiet', 'check-update']
if __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '7':
# This feature is disable because it is not used by Salt and lasts a lot with using large repo like EPEL
if __grains__.get('os_family') == 'RedHat' \
and __grains__.get('osmajorrelease') == 7:
# This feature is disabled because it is not used by Salt and adds a
# lot of extra time to the command with large repos like EPEL
update_cmd.append('--setopt=autocheck_running_kernel=false')
for args in (repo_arg, exclude_arg, branch_arg):
if args:
clean_cmd.extend(args)
update_cmd.extend(args)
clean_cmd.extend(options)
update_cmd.extend(options)
__salt__['cmd.run'](clean_cmd, python_shell=False)
if check_update_:
@ -1090,6 +1072,7 @@ def install(name=None,
reinstall=False,
normalize=True,
update_holds=False,
saltenv='base',
**kwargs):
'''
.. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
@ -1227,9 +1210,7 @@ def install(name=None,
{'<package>': {'old': '<old-version>',
'new': '<new-version>'}}
'''
repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs)
branch_arg = _get_branch_option(**kwargs)
options = _get_options(**kwargs)
if salt.utils.is_true(refresh):
refresh_db(**kwargs)
@ -1237,7 +1218,7 @@ def install(name=None,
try:
pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](
name, pkgs, sources, normalize=normalize, **kwargs
name, pkgs, sources, saltenv=saltenv, normalize=normalize
)
except MinionError as exc:
raise CommandExecutionError(exc)
@ -1439,9 +1420,7 @@ def install(name=None,
'''
DRY function to add args common to all yum/dnf commands
'''
for arg in (repo_arg, exclude_arg, branch_arg):
if arg:
cmd.extend(arg)
cmd.extend(options)
if skip_verify:
cmd.append('--nogpgcheck')
if downloadonly:
@ -1713,10 +1692,7 @@ def upgrade(name=None,
salt '*' pkg.upgrade security=True exclude='kernel*'
'''
repo_arg = _get_repo_options(**kwargs)
exclude_arg = _get_excludes_option(**kwargs)
branch_arg = _get_branch_option(**kwargs)
extra_args = _get_extra_options(**kwargs)
options = _get_options(get_extra_options=True, **kwargs)
if salt.utils.is_true(refresh):
refresh_db(**kwargs)
@ -1745,9 +1721,7 @@ def upgrade(name=None,
and __salt__['config.get']('systemd.scope', True):
cmd.extend(['systemd-run', '--scope'])
cmd.extend([_yum(), '--quiet', '-y'])
for args in (repo_arg, exclude_arg, branch_arg, extra_args):
if args:
cmd.extend(args)
cmd.extend(options)
if skip_verify:
cmd.append('--nogpgcheck')
cmd.append('upgrade')

View File

@ -0,0 +1,441 @@
# -*- coding: utf-8 -*-
# Import Python Libs
from __future__ import absolute_import
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
Mock,
MagicMock,
patch,
NO_MOCK,
NO_MOCK_REASON
)
# Import Salt libs
import salt.modules.yumpkg as yumpkg
LIST_REPOS = {
'base': {
'file': '/etc/yum.repos.d/CentOS-Base.repo',
'gpgcheck': '1',
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra',
'name': 'CentOS-$releasever - Base'
},
'base-source': {
'baseurl': 'http://vault.centos.org/centos/$releasever/os/Source/',
'enabled': '0',
'file': '/etc/yum.repos.d/CentOS-Sources.repo',
'gpgcheck': '1',
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
'name': 'CentOS-$releasever - Base Sources'
},
'updates': {
'file': '/etc/yum.repos.d/CentOS-Base.repo',
'gpgcheck': '1',
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra',
'name': 'CentOS-$releasever - Updates'
},
'updates-source': {
'baseurl': 'http://vault.centos.org/centos/$releasever/updates/Source/',
'enabled': '0',
'file': '/etc/yum.repos.d/CentOS-Sources.repo',
'gpgcheck': '1',
'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7',
'name': 'CentOS-$releasever - Updates Sources'
}
}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class YumTestCase(TestCase, LoaderModuleMockMixin):
'''
Test cases for salt.modules.yumpkg
'''
def setup_loader_modules(self):
return {
yumpkg: {
'__context__': {
'yum_bin': 'yum',
},
'__grains__': {
'osarch': 'x86_64',
'os_family': 'RedHat',
'osmajorrelease': 7,
},
}
}
def test_latest_version_with_options(self):
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})):
# with fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.latest_version(
'foo',
refresh=False,
fromrepo='good',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '--disablerepo=*', '--enablerepo=good',
'--branch=foo', 'list', 'available', 'foo'],
ignore_retcode=True,
output_loglevel='trace',
python_shell=False)
# without fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.latest_version(
'foo',
refresh=False,
enablerepo='good',
disablerepo='bad',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good',
'--branch=foo', 'list', 'available', 'foo'],
ignore_retcode=True,
output_loglevel='trace',
python_shell=False)
def test_list_repo_pkgs_with_options(self):
'''
Test list_repo_pkgs with and without fromrepo
NOTE: mock_calls is a stack. The most recent call is indexed
with 0, while the first call would have the highest index.
'''
really_old_yum = MagicMock(return_value='3.2.0')
older_yum = MagicMock(return_value='3.4.0')
newer_yum = MagicMock(return_value='3.4.5')
list_repos_mock = MagicMock(return_value=LIST_REPOS)
kwargs = {'output_loglevel': 'trace',
'ignore_retcode': True,
'python_shell': False}
with patch.object(yumpkg, 'list_repos', list_repos_mock):
# Test with really old yum. The fromrepo argument has no effect on
# the yum commands we'd run.
with patch.dict(yumpkg.__salt__, {'cmd.run': really_old_yum}):
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_repo_pkgs('foo')
# We should have called cmd.run_all twice
self.assertEqual(len(cmd.mock_calls), 2)
# Check args from first call
self.assertEqual(
cmd.mock_calls[1][1],
(['yum', '--quiet', 'list', 'available'],)
)
# Check kwargs from first call
self.assertEqual(cmd.mock_calls[1][2], kwargs)
# Check args from second call
self.assertEqual(
cmd.mock_calls[0][1],
(['yum', '--quiet', 'list', 'installed'],)
)
# Check kwargs from second call
self.assertEqual(cmd.mock_calls[0][2], kwargs)
# Test with really old yum. The fromrepo argument has no effect on
# the yum commands we'd run.
with patch.dict(yumpkg.__salt__, {'cmd.run': older_yum}):
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_repo_pkgs('foo')
# We should have called cmd.run_all twice
self.assertEqual(len(cmd.mock_calls), 2)
# Check args from first call
self.assertEqual(
cmd.mock_calls[1][1],
(['yum', '--quiet', '--showduplicates', 'list', 'available'],)
)
# Check kwargs from first call
self.assertEqual(cmd.mock_calls[1][2], kwargs)
# Check args from second call
self.assertEqual(
cmd.mock_calls[0][1],
(['yum', '--quiet', '--showduplicates', 'list', 'installed'],)
)
# Check kwargs from second call
self.assertEqual(cmd.mock_calls[0][2], kwargs)
# Test with newer yum. We should run one yum command per repo, so
# fromrepo would limit how many calls we make.
with patch.dict(yumpkg.__salt__, {'cmd.run': newer_yum}):
# When fromrepo is used, we would only run one yum command, for
# that specific repo.
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_repo_pkgs('foo', fromrepo='base')
# We should have called cmd.run_all once
self.assertEqual(len(cmd.mock_calls), 1)
# Check args
self.assertEqual(
cmd.mock_calls[0][1],
(['yum', '--quiet', '--showduplicates',
'repository-packages', 'base', 'list', 'foo'],)
)
# Check kwargs
self.assertEqual(cmd.mock_calls[0][2], kwargs)
# Test enabling base-source and disabling updates. We should
# get two calls, one for each enabled repo. Because dict
# iteration order will vary, different Python versions will be
# do them in different orders, which is OK, but it will just
# mean that we will have to check both the first and second
# mock call both times.
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_repo_pkgs(
'foo',
enablerepo='base-source',
disablerepo='updates')
# We should have called cmd.run_all twice
self.assertEqual(len(cmd.mock_calls), 2)
for repo in ('base', 'base-source'):
for index in (0, 1):
try:
# Check args
self.assertEqual(
cmd.mock_calls[index][1],
(['yum', '--quiet', '--showduplicates',
'repository-packages', repo, 'list',
'foo'],)
)
# Check kwargs
self.assertEqual(cmd.mock_calls[index][2], kwargs)
break
except AssertionError:
continue
else:
self.fail("repo '{0}' not checked".format(repo))
def test_list_upgrades_dnf(self):
'''
The subcommand should be "upgrades" with dnf
'''
with patch.dict(yumpkg.__context__, {'yum_bin': 'dnf'}):
# with fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_upgrades(
refresh=False,
fromrepo='good',
branch='foo')
cmd.assert_called_once_with(
['dnf', '--quiet', '--disablerepo=*', '--enablerepo=good',
'--branch=foo', 'list', 'upgrades'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
# without fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_upgrades(
refresh=False,
enablerepo='good',
disablerepo='bad',
branch='foo')
cmd.assert_called_once_with(
['dnf', '--quiet', '--disablerepo=bad', '--enablerepo=good',
'--branch=foo', 'list', 'upgrades'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
def test_list_upgrades_yum(self):
'''
The subcommand should be "updates" with yum
'''
# with fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_upgrades(
refresh=False,
fromrepo='good',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '--disablerepo=*', '--enablerepo=good',
'--branch=foo', 'list', 'updates'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
# without fromrepo
cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.list_upgrades(
refresh=False,
enablerepo='good',
disablerepo='bad',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good',
'--branch=foo', 'list', 'updates'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
def test_refresh_db_with_options(self):
with patch('salt.utils.pkg.clear_rtag', Mock()):
# With check_update=True we will do a cmd.run to run the clean_cmd, and
# then a separate cmd.retcode to check for updates.
# with fromrepo
clean_cmd = Mock()
update_cmd = MagicMock(return_value=0)
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd,
'cmd.retcode': update_cmd}):
yumpkg.refresh_db(
check_update=True,
fromrepo='good',
branch='foo')
clean_cmd.assert_called_once_with(
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=*',
'--enablerepo=good', '--branch=foo'],
python_shell=False)
update_cmd.assert_called_once_with(
['yum', '--quiet', 'check-update',
'--setopt=autocheck_running_kernel=false', '--disablerepo=*',
'--enablerepo=good', '--branch=foo'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
# without fromrepo
clean_cmd = Mock()
update_cmd = MagicMock(return_value=0)
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd,
'cmd.retcode': update_cmd}):
yumpkg.refresh_db(
check_update=True,
enablerepo='good',
disablerepo='bad',
branch='foo')
clean_cmd.assert_called_once_with(
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=bad',
'--enablerepo=good', '--branch=foo'],
python_shell=False)
update_cmd.assert_called_once_with(
['yum', '--quiet', 'check-update',
'--setopt=autocheck_running_kernel=false', '--disablerepo=bad',
'--enablerepo=good', '--branch=foo'],
output_loglevel='trace',
ignore_retcode=True,
python_shell=False)
# With check_update=False we will just do a cmd.run for the clean_cmd
# with fromrepo
clean_cmd = Mock()
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd}):
yumpkg.refresh_db(
check_update=False,
fromrepo='good',
branch='foo')
clean_cmd.assert_called_once_with(
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=*',
'--enablerepo=good', '--branch=foo'],
python_shell=False)
# without fromrepo
clean_cmd = Mock()
with patch.dict(yumpkg.__salt__, {'cmd.run': clean_cmd}):
yumpkg.refresh_db(
check_update=False,
enablerepo='good',
disablerepo='bad',
branch='foo')
clean_cmd.assert_called_once_with(
['yum', '--quiet', 'clean', 'expire-cache', '--disablerepo=bad',
'--enablerepo=good', '--branch=foo'],
python_shell=False)
def test_install_with_options(self):
parse_targets = MagicMock(return_value=({'foo': None}, 'repository'))
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \
patch.object(yumpkg, 'list_holds', MagicMock(return_value=[])), \
patch.dict(yumpkg.__salt__, {'pkg_resource.parse_targets': parse_targets}), \
patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)):
# with fromrepo
cmd = MagicMock(return_value={'retcode': 0})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.install(
refresh=False,
fromrepo='good',
branch='foo')
cmd.assert_called_once_with(
['yum', '-y', '--disablerepo=*', '--enablerepo=good',
'--branch=foo', 'install', 'foo'],
output_loglevel='trace',
python_shell=False,
redirect_stderr=True)
# without fromrepo
cmd = MagicMock(return_value={'retcode': 0})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.install(
refresh=False,
enablerepo='good',
disablerepo='bad',
branch='foo')
cmd.assert_called_once_with(
['yum', '-y', '--disablerepo=bad', '--enablerepo=good',
'--branch=foo', 'install', 'foo'],
output_loglevel='trace',
python_shell=False,
redirect_stderr=True)
def test_upgrade_with_options(self):
with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \
patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)):
# with fromrepo
cmd = MagicMock(return_value={'retcode': 0})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.upgrade(
refresh=False,
fromrepo='good',
exclude='kernel*',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '-y', '--disablerepo=*', '--enablerepo=good',
'--branch=foo', '--exclude=kernel*', 'upgrade'],
output_loglevel='trace',
python_shell=False)
# without fromrepo
cmd = MagicMock(return_value={'retcode': 0})
with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}):
yumpkg.upgrade(
refresh=False,
enablerepo='good',
disablerepo='bad',
exclude='kernel*',
branch='foo')
cmd.assert_called_once_with(
['yum', '--quiet', '-y', '--disablerepo=bad', '--enablerepo=good',
'--branch=foo', '--exclude=kernel*', 'upgrade'],
output_loglevel='trace',
python_shell=False)