Skip to content

Commit

Permalink
Add repository-packages remove-or-distro-sync command. (RhBug:908764)
Browse files Browse the repository at this point in the history
  • Loading branch information
radekholy24 committed Aug 26, 2014
1 parent 031f95b commit 9abbc73
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 4 deletions.
84 changes: 80 additions & 4 deletions dnf/cli/commands/__init__.py
Expand Up @@ -741,6 +741,81 @@ def run(self, reponame, cli_args):
else:
raise dnf.exceptions.Error(_('Nothing to do.'))

class RemoveOrDistroSyncSubCommand(SubCommand):
"""Implementation of the remove-or-distro-sync sub-command."""

activate_sack = True

aliases = ('remove-or-distro-sync',)

resolve = True

writes_rpmdb = True

def check(self, cli_args):
"""Verify whether the command can run with given arguments."""
super(RepoPkgsCommand.RemoveOrDistroSyncSubCommand, self).check(
cli_args)
checkGPGKey(self.base, self.cli)

@staticmethod
def parse_arguments(cli_args):
"""Parse command arguments."""
return cli_args

def _replace(self, pkg_spec, reponame):
"""Synchronize a package with another repository or remove it."""
self.cli.base.sack.disable_repo(reponame)

subject = dnf.subject.Subject(pkg_spec)
matches = subject.get_best_query(self.cli.base.sack)
yumdb = self.cli.base.yumdb
installed = [
pkg for pkg in matches.installed()
if yumdb.get_package(pkg).get('from_repo') == reponame]
if not installed:
raise dnf.exceptions.PackagesNotInstalledError(
'no package matched', pkg_spec)
available = matches.available()
clean_deps = self.cli.base.conf.clean_requirements_on_remove
for package in installed:
if available.filter(name=package.name, arch=package.arch):
self.cli.base.goal.distupgrade(package)
else:
self.cli.base.goal.erase(package, clean_deps=clean_deps)

def run(self, reponame, cli_args):
"""Execute the command with respect to given arguments *cli_args*."""
super(RepoPkgsCommand.RemoveOrDistroSyncSubCommand, self).run(
cli_args)
self.check(cli_args)
pkg_specs = self.parse_arguments(cli_args)

done = False

if not pkg_specs:
# Sync all packages.
try:
self._replace('*', reponame)
except dnf.exceptions.PackagesNotInstalledError:
msg = _('No package installed from the repository.')
logger.info(msg)
else:
done = True
else:
# Reinstall packages.
for pkg_spec in pkg_specs:
try:
self._replace(pkg_spec, reponame)
except dnf.exceptions.PackagesNotInstalledError:
msg = _('No match for argument: %s')
logger.info(msg, pkg_spec)
else:
done = True

if not done:
raise dnf.exceptions.Error(_('Nothing to do.'))

class RemoveOrReinstallSubCommand(SubCommand):
"""Implementation of the remove-or-reinstall sub-command."""

Expand Down Expand Up @@ -933,15 +1008,16 @@ def run(self, reponame, cli_args):

SUBCMDS = {CheckUpdateSubCommand, InfoSubCommand, InstallSubCommand,
ListSubCommand, MoveToSubCommand, ReinstallOldSubCommand,
ReinstallSubCommand, RemoveOrReinstallSubCommand,
RemoveSubCommand, UpgradeSubCommand, UpgradeToSubCommand}
ReinstallSubCommand, RemoveOrDistroSyncSubCommand,
RemoveOrReinstallSubCommand, RemoveSubCommand,
UpgradeSubCommand, UpgradeToSubCommand}

aliases = ('repository-packages',
'repo-pkgs', 'repo-packages', 'repository-pkgs')
summary = _('Run commands on top of all packages in given repository')
usage = '%s check-update|info|install|list|move-to|reinstall|' \
'reinstall-old|remove|remove-or-reinstall|upgrade|' \
'upgrade-to [%s...]' % (_('REPO'), _('ARG'))
'reinstall-old|remove|remove-or-distro-sync|remove-or-reinstall|' \
'upgrade|upgrade-to [%s...]' % (_('REPO'), _('ARG'))

def __init__(self, cli):
"""Initialize the command."""
Expand Down
3 changes: 3 additions & 0 deletions doc/command_ref.rst
Expand Up @@ -508,6 +508,9 @@ The repository-packages command allows the user to run commands on top of all pa
``dnf [options] repository-packages <repoid> remove [<package-name-spec>...]``
Remove all packages installed from the repository along with any packages depending on the packages being removed. If ``clean_requirements_on_remove`` is enabled (the default) also removes any dependencies that are no longer needed.

``dnf [options] repository-packages <repoid> remove-or-distro-sync [<package-name-spec>...]``
Select all packages installed from the repository. Upgrade, downgrade or keep those of them that are available in another repositories to match the latest version available there and remove the others along with any packages depending on the packages being removed. If ``clean_requirements_on_remove`` is enabled (the default) also removes any dependencies that are no longer needed.

``dnf [options] repository-packages <repoid> remove-or-reinstall [<package-name-spec>...]``
Select all packages installed from the repository. Reinstall those of them that are available in another repositories and remove the others along with any packages depending on the packages being removed. If ``clean_requirements_on_remove`` is enabled (the default) also removes any dependencies that are no longer needed.

Expand Down
86 changes: 86 additions & 0 deletions tests/test_commands.py
Expand Up @@ -692,6 +692,92 @@ def test_all_reinstallold(self):
self.assertEqual(self.mock.mock_calls,
[mock.call.reinstall_old_run('main', [])])

class RepoPkgsRemoveOrDistroSyncSubCommandTest(support.ResultTestCase):

"""Tests of ``RemoveOrDistroSyncSubCommand`` class."""

def setUp(self):
"""Prepare the test fixture."""
super(RepoPkgsRemoveOrDistroSyncSubCommandTest, self).setUp()
self.cli = support.BaseCliStub('distro').mock_cli()
self.cli.base.init_sack()

def test_run_spec_sync(self):
"""Test running with a package which can be synchronized."""
for pkg in self.cli.base.sack.query().installed():
data = support.RPMDBAdditionalDataPackageStub()
data.from_repo = 'non-distro' if pkg.name == 'pepper' else 'distro'
self.cli.base.yumdb.db[str(pkg)] = data

cmd = dnf.cli.commands.RepoPkgsCommand.RemoveOrDistroSyncSubCommand(
self.cli)
cmd.run('non-distro', ['pepper'])

self.assertResult(self.cli.base, itertools.chain(
self.cli.base.sack.query().installed().filter(name__neq='pepper'),
dnf.subject.Subject('pepper').get_best_query(self.cli.base.sack)
.available()))

def test_run_spec_remove(self):
"""Test running with a package which must be removed."""
for pkg in self.cli.base.sack.query().installed():
data = support.RPMDBAdditionalDataPackageStub()
data.from_repo = 'non-distro' if pkg.name == 'hole' else 'distro'
self.cli.base.yumdb.db[str(pkg)] = data

cmd = dnf.cli.commands.RepoPkgsCommand.RemoveOrDistroSyncSubCommand(
self.cli)
cmd.run('non-distro', ['hole'])

self.assertResult(
self.cli.base,
self.cli.base.sack.query().installed().filter(name__neq='hole'))

def test_run_all(self):
"""Test running without a package specification."""
nondist = {'pepper', 'hole'}
for pkg in self.cli.base.sack.query().installed():
data = support.RPMDBAdditionalDataPackageStub()
data.from_repo = 'non-distro' if pkg.name in nondist else 'distro'
self.cli.base.yumdb.db[str(pkg)] = data

cmd = dnf.cli.commands.RepoPkgsCommand.RemoveOrDistroSyncSubCommand(
self.cli)
cmd.run('non-distro', [])

self.assertResult(self.cli.base, itertools.chain(
self.cli.base.sack.query().installed().filter(name__neq='pepper')
.filter(name__neq='hole'),
dnf.subject.Subject('pepper').get_best_query(self.cli.base.sack)
.available()))

@mock.patch('dnf.cli.commands._', dnf.pycomp.NullTranslations().ugettext)
def test_run_spec_notinstalled(self):
"""Test running with a package which is not installed."""
stdout = dnf.pycomp.StringIO()

cmd = dnf.cli.commands.RepoPkgsCommand.RemoveOrDistroSyncSubCommand(
self.cli)
with support.wiretap_logs('dnf', logging.INFO, stdout):
self.assertRaises(dnf.exceptions.Error,
cmd.run, 'non-distro', ['not-installed'])

self.assertIn('No match for argument: not-installed\n', stdout.getvalue(),
'mismatch not logged')

@mock.patch('dnf.cli.commands._', dnf.pycomp.NullTranslations().ugettext)
def test_run_all_notinstalled(self):
"""Test running with a repository from which nothing is installed."""
stdout = dnf.pycomp.StringIO()

cmd = dnf.cli.commands.RepoPkgsCommand.RemoveOrDistroSyncSubCommand(
self.cli)
with support.wiretap_logs('dnf', logging.INFO, stdout):
self.assertRaises(dnf.exceptions.Error, cmd.run, 'non-distro', [])

self.assertIn('No package installed from the repository.\n',
stdout.getvalue(), 'mismatch not logged')

class RepoPkgsRemoveOrReinstallSubCommandTest(support.ResultTestCase):

"""Tests of ``dnf.cli.commands.RepoPkgsCommand.RemoveOrReinstallSubCommand`` class."""
Expand Down

0 comments on commit 9abbc73

Please sign in to comment.