From 64cea41f6b0ca4a736800f1895d995a17a593a79 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Thu, 8 Nov 2018 17:15:11 -0600 Subject: [PATCH] dnf to support modularity module appstream specs Fixes #48743 Signed-off-by: Adam Miller (cherry picked from commit 4a06e9567134e79d85ba5a27c8960775d5bbcd73) fix up sanity tests and with_modules conditional Signed-off-by: Adam Miller (cherry picked from commit 23b60035a192dcf7f33cc7bf9192e4d342443809) fix yamllint sanity Signed-off-by: Adam Miller (cherry picked from commit 2b878546d3189834386d5f2a2c445fad69313eaa) fix group-missing/invalid-group detection Signed-off-by: Adam Miller (cherry picked from commit 41176b5e0f302bc4d8b89070f3df53d0b8ef0994) --- changelogs/fragments/dnf-modularity.yaml | 3 + lib/ansible/modules/packaging/os/dnf.py | 119 ++++++++++++++++-- test/integration/targets/dnf/tasks/main.yml | 5 + .../targets/dnf/tasks/modularity.yml | 47 +++++++ 4 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/dnf-modularity.yaml create mode 100644 test/integration/targets/dnf/tasks/modularity.yml diff --git a/changelogs/fragments/dnf-modularity.yaml b/changelogs/fragments/dnf-modularity.yaml new file mode 100644 index 00000000000..3c4c64ac7c7 --- /dev/null +++ b/changelogs/fragments/dnf-modularity.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - "dnf properly support modularity appstream installation via overloaded group modifier syntax" diff --git a/lib/ansible/modules/packaging/os/dnf.py b/lib/ansible/modules/packaging/os/dnf.py index 5a15eeb73ce..a3da7b5fc6c 100644 --- a/lib/ansible/modules/packaging/os/dnf.py +++ b/lib/ansible/modules/packaging/os/dnf.py @@ -248,6 +248,21 @@ EXAMPLES = ''' name: httpd state: absent autoremove: no + +- name: install a modularity appstream with defined stream and profile + dnf: + name: '@postgresql:9.6/client' + state: present + +- name: install a modularity appstream with defined stream + dnf: + name: '@postgresql:9.6' + state: present + +- name: install a modularity appstream with defined profile + dnf: + name: '@postgresql/client' + state: present ''' import os @@ -288,6 +303,11 @@ class DnfModule(YumDnf): self._ensure_dnf() + try: + self.with_modules = dnf.base.WITH_MODULES + except AttributeError: + self.with_modules = False + def _sanitize_dnf_error_msg(self, spec, error): """ For unhandled dnf.exceptions.Error scenarios, there are certain error @@ -696,17 +716,32 @@ class DnfModule(YumDnf): } def _parse_spec_group_file(self): - pkg_specs, grp_specs, filenames = [], [], [] + pkg_specs, grp_specs, module_specs, filenames = [], [], [], [] + already_loaded_comps = False # Only load this if necessary, it's slow + for name in self.names: if name.endswith(".rpm"): if '://' in name: name = self.fetch_rpm_from_url(name) filenames.append(name) - elif name.startswith("@"): - grp_specs.append(name[1:]) + elif name.startswith("@") or ('/' in name): + if not already_loaded_comps: + self.base.read_comps() + already_loaded_comps = True + + grp_env_mdl_candidate = name[1:].strip() + + if self.with_modules: + mdl = self.module_base._get_modules(grp_env_mdl_candidate) + if mdl[0]: + module_specs.append(grp_env_mdl_candidate) + else: + grp_specs.append(grp_env_mdl_candidate) + else: + grp_specs.append(grp_env_mdl_candidate) else: pkg_specs.append(name) - return pkg_specs, grp_specs, filenames + return pkg_specs, grp_specs, module_specs, filenames def _update_only(self, pkgs): not_installed = [] @@ -760,6 +795,16 @@ class DnfModule(YumDnf): rc=1, ) + def _is_module_installed(self, module_spec): + if self.with_modules: + module_spec = module_spec.strip() + module_list, nsv = self.module_base._get_modules(module_spec) + + if nsv.stream in self.base._moduleContainer.getEnabledStream(nsv.name): + return True + + return False # seems like a sane default + def ensure(self): allow_erasing = False @@ -792,9 +837,7 @@ class DnfModule(YumDnf): failure_response['msg'] = "Depsolve Error occured attempting to upgrade all packages" self.module.fail_json(**failure_response) else: - pkg_specs, group_specs, filenames = self._parse_spec_group_file() - if group_specs: - self.base.read_comps() + pkg_specs, group_specs, module_specs, filenames = self._parse_spec_group_file() pkg_specs = [p.strip() for p in pkg_specs] filenames = [f.strip() for f in filenames] @@ -820,6 +863,23 @@ class DnfModule(YumDnf): for filename in filenames: response['results'].append("Installed {0}".format(filename)) + # Install modules + if module_specs and self.with_modules: + for module in module_specs: + try: + if not self._is_module_installed(module): + response['results'].append("Module {0} installed.".format(module)) + self.module_base.install([module]) + except dnf.exceptions.MarkingErrors as e: + failure_response['failures'].append( + " ".join( + ( + ' '.join(module), + to_native(e) + ) + ) + ) + # Install groups. for group in groups: try: @@ -846,6 +906,13 @@ class DnfModule(YumDnf): except dnf.exceptions.Error as e: failure_response['failures'].append(" ".join((environment, to_native(e)))) + if module_specs and not self.with_modules: + # This means that the group or env wasn't found in comps + self.module.fail_json( + msg="No group {0} available.".format(module_specs[0]), + results=[], + ) + # Install packages. if self.update_only: not_installed = self._update_only(pkg_specs) @@ -866,6 +933,23 @@ class DnfModule(YumDnf): for filename in filenames: response['results'].append("Installed {0}".format(filename)) + # Upgrade modules + if module_specs and self.with_modules: + for module in module_specs: + try: + if self._is_module_installed(module): + response['results'].append("Module {0} upgraded.".format(module)) + self.module_base.upgrade([module]) + except dnf.exceptions.MarkingErrors as e: + failure_response['failures'].append( + " ".join( + ( + ' '.join(module), + to_native(e) + ) + ) + ) + for group in groups: try: try: @@ -918,6 +1002,24 @@ class DnfModule(YumDnf): results=[], ) + # Remove modules + if module_specs and self.with_modules: + for module in module_specs: + try: + if self._is_module_installed(module): + response['results'].append("Module {0} removed.".format(module)) + self.module_base.disable([module]) + self.module_base.remove([module]) + except dnf.exceptions.MarkingErrors as e: + failure_response['failures'].append( + " ".join( + ( + ' '.join(module), + to_native(e) + ) + ) + ) + for group in groups: try: self.base.group_remove(group) @@ -1044,6 +1146,9 @@ class DnfModule(YumDnf): self.enablerepo, self.installroot ) + if self.with_modules: + self.module_base = dnf.module.module_base.ModuleBase(self.base) + self.ensure() diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml index f4931df3138..aba860fe5a1 100644 --- a/test/integration/targets/dnf/tasks/main.yml +++ b/test/integration/targets/dnf/tasks/main.yml @@ -38,3 +38,8 @@ when: - ansible_distribution == 'Fedora' - ansible_distribution_major_version|int >= 23 + +- import_tasks: 'modularity.yml' + when: + - ansible_distribution == 'Fedora' and ansible_distribution_major_version|int >= 29 + - ansible_distribution == 'RedHat' and ansible_distribution_major_version|int >= 8 diff --git a/test/integration/targets/dnf/tasks/modularity.yml b/test/integration/targets/dnf/tasks/modularity.yml new file mode 100644 index 00000000000..c520fe5138c --- /dev/null +++ b/test/integration/targets/dnf/tasks/modularity.yml @@ -0,0 +1,47 @@ +- name: install "@postgresql:9.6/client" module + dnf: + name: "@postgresql:9.6/client" + state: present + register: dnf_result + +- name: verify installation of "@postgresql:9.6/client" module + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: install "@postgresql:9.6/client" module again + dnf: + name: "@postgresql:9.6/client" + state: present + register: dnf_result + +- name: verify installation of "@postgresql:9.6/client" module again + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed" + +- name: uninstall "@postgresql:9.6/client" module + dnf: + name: "@postgresql:9.6/client" + state: absent + register: dnf_result + +- name: verify uninstallation of "@postgresql:9.6/client" module + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: uninstall "@postgresql:9.6/client" module again + dnf: + name: "@postgresql:9.6/client" + state: install + register: dnf_result + +- name: verify uninstallation of "@postgresql:9.6/client" module again + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed"