diff --git a/changelogs/fragments/82461-dnf-provides.yml b/changelogs/fragments/82461-dnf-provides.yml new file mode 100644 index 00000000000..b7327a383d6 --- /dev/null +++ b/changelogs/fragments/82461-dnf-provides.yml @@ -0,0 +1,2 @@ +bugfixes: + - dnf - fix an issue when installing a package by specifying a file it provides could result in installing a different package providing the same file than the package already installed resulting in resolution failure (https://github.com/ansible/ansible/issues/82461) diff --git a/lib/ansible/modules/dnf.py b/lib/ansible/modules/dnf.py index 28877986cc2..1780c2f8650 100644 --- a/lib/ansible/modules/dnf.py +++ b/lib/ansible/modules/dnf.py @@ -921,17 +921,6 @@ class DnfModule(YumDnf): return {'failed': False, 'msg': msg, 'failure': '', 'rc': 0} - def _whatprovides(self, filepath): - self.base.read_all_repos() - available = self.base.sack.query().available() - # Search in file - files_filter = available.filter(file=filepath) - # And Search in provides - pkg_spec = files_filter.union(available.filter(provides=filepath)).run() - - if pkg_spec: - return pkg_spec[0].name - def _parse_spec_group_file(self): pkg_specs, grp_specs, module_specs, filenames = [], [], [], [] already_loaded_comps = False # Only load this if necessary, it's slow @@ -943,11 +932,13 @@ class DnfModule(YumDnf): elif name.endswith(".rpm"): filenames.append(name) elif name.startswith('/'): - # like "dnf install /usr/bin/vi" - pkg_spec = self._whatprovides(name) - if pkg_spec: - pkg_specs.append(pkg_spec) - continue + # dnf install /usr/bin/vi + installed = self.base.sack.query().filter(provides=name, file=name).installed().run() + if installed: + pkg_specs.append(installed[0].name) # should be only one? + elif not self.update_only: + # not installed, pass the filename for dnf to process + pkg_specs.append(name) elif name.startswith("@") or ('/' in name): if not already_loaded_comps: self.base.read_comps() diff --git a/test/integration/targets/dnf/tasks/repo.yml b/test/integration/targets/dnf/tasks/repo.yml index 2210002cab0..529bc905835 100644 --- a/test/integration/targets/dnf/tasks/repo.yml +++ b/test/integration/targets/dnf/tasks/repo.yml @@ -435,3 +435,35 @@ dnf: name: dinginessentail state: absent + +- name: > + test that when a package providing a file is installed then installing by specifying the file doesn't result in + installing a different package providing the same file + block: + - dnf: + name: provides_foo_b + state: "{{ item }}" + loop: + - absent + - present + + - dnf: + name: /foo.so + state: present + register: dnf_result + + - command: rpm -q package_foo_a + ignore_errors: true + register: rpm_result + + - assert: + that: + - dnf_result is not changed + - rpm_result.rc == 1 + always: + - name: Clean up + dnf: + name: "{{ item }}" + state: absent + loop: + - provides_foo_b diff --git a/test/integration/targets/setup_rpm_repo/library/create_repo.py b/test/integration/targets/setup_rpm_repo/library/create_repo.py index 8af3711ad86..8ca4a82e4cc 100644 --- a/test/integration/targets/setup_rpm_repo/library/create_repo.py +++ b/test/integration/targets/setup_rpm_repo/library/create_repo.py @@ -12,10 +12,12 @@ from ansible.module_utils.common.respawn import has_respawned, probe_interpreter HAS_RPMFLUFF = True can_use_rpm_weak_deps = None try: - from rpmfluff import SimpleRpmBuild + from rpmfluff import SimpleRpmBuild, GeneratedSourceFile, make_elf from rpmfluff import YumRepoBuild except ImportError: try: + from rpmfluff.make import make_elf + from rpmfluff.sourcefile import GeneratedSourceFile from rpmfluff.rpmbuild import SimpleRpmBuild from rpmfluff.yumrepobuild import YumRepoBuild except ImportError: @@ -32,20 +34,21 @@ if HAS_RPMFLUFF: pass -RPM = namedtuple('RPM', ['name', 'version', 'release', 'epoch', 'recommends', 'arch']) - +RPM = namedtuple('RPM', ['name', 'version', 'release', 'epoch', 'recommends', 'file', 'arch']) SPECS = [ - RPM('dinginessentail', '1.0', '1', None, None, None), - RPM('dinginessentail', '1.0', '2', '1', None, None), - RPM('dinginessentail', '1.1', '1', '1', None, None), - RPM('dinginessentail-olive', '1.0', '1', None, None, None), - RPM('dinginessentail-olive', '1.1', '1', None, None, None), - RPM('landsidescalping', '1.0', '1', None, None, None), - RPM('landsidescalping', '1.1', '1', None, None, None), - RPM('dinginessentail-with-weak-dep', '1.0', '1', None, ['dinginessentail-weak-dep'], None), - RPM('dinginessentail-weak-dep', '1.0', '1', None, None, None), - RPM('noarchfake', '1.0', '1', None, None, 'noarch'), + RPM('dinginessentail', '1.0', '1', None, None, None, None), + RPM('dinginessentail', '1.0', '2', '1', None, None, None), + RPM('dinginessentail', '1.1', '1', '1', None, None, None), + RPM('dinginessentail-olive', '1.0', '1', None, None, None, None), + RPM('dinginessentail-olive', '1.1', '1', None, None, None, None), + RPM('landsidescalping', '1.0', '1', None, None, None, None), + RPM('landsidescalping', '1.1', '1', None, None, None, None), + RPM('dinginessentail-with-weak-dep', '1.0', '1', None, ['dinginessentail-weak-dep'], None, None), + RPM('dinginessentail-weak-dep', '1.0', '1', None, None, None, None), + RPM('noarchfake', '1.0', '1', None, None, None, 'noarch'), + RPM('provides_foo_a', '1.0', '1', None, None, 'foo.so', 'noarch'), + RPM('provides_foo_b', '1.0', '1', None, None, 'foo.so', 'noarch'), ] @@ -63,6 +66,14 @@ def create_repo(arch='x86_64'): for recommend in spec.recommends: pkg.add_recommends(recommend) + if spec.file: + pkg.add_installed_file( + "/" + spec.file, + GeneratedSourceFile( + spec.file, make_elf() + ) + ) + pkgs.append(pkg) repo = YumRepoBuild(pkgs)