diff options
Diffstat (limited to '')
-rw-r--r-- | test/integration/targets/dnf/tasks/cacheonly.yml | 16 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/dnf.yml | 834 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/dnfinstallroot.yml | 35 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/dnfreleasever.yml | 47 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/filters.yml | 162 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/filters_check_mode.yml | 118 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/gpg.yml | 88 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/logging.yml | 48 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/main.yml | 74 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/modularity.yml | 104 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/repo.yml | 309 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml | 318 | ||||
-rw-r--r-- | test/integration/targets/dnf/tasks/test_sos_removal.yml | 19 |
13 files changed, 2172 insertions, 0 deletions
diff --git a/test/integration/targets/dnf/tasks/cacheonly.yml b/test/integration/targets/dnf/tasks/cacheonly.yml new file mode 100644 index 0000000..eb19156 --- /dev/null +++ b/test/integration/targets/dnf/tasks/cacheonly.yml @@ -0,0 +1,16 @@ +--- +- name: Test cacheonly (clean before testing) + command: dnf clean all + +- name: Try installing from cache where it has been cleaned + dnf: + name: sos + state: latest + cacheonly: true + register: dnf_result + ignore_errors: true + +- name: Verify dnf failed or has not changed + assert: + that: + - "dnf_result is failed or not dnf_result is changed" diff --git a/test/integration/targets/dnf/tasks/dnf.yml b/test/integration/targets/dnf/tasks/dnf.yml new file mode 100644 index 0000000..ec1c36f --- /dev/null +++ b/test/integration/targets/dnf/tasks/dnf.yml @@ -0,0 +1,834 @@ +# UNINSTALL 'python2-dnf' +# The `dnf` module has the smarts to auto-install the relevant python +# bindings. To test, we will first uninstall python2-dnf (so that the tests +# on python2 will require python2-dnf) +- name: check python2-dnf with rpm + shell: rpm -q python2-dnf + register: rpm_result + ignore_errors: true + +# Don't uninstall python2-dnf with the `dnf` module in case it needs to load +# some dnf python files after the package is uninstalled. +- name: uninstall python2-dnf with shell + shell: dnf -y remove python2-dnf + when: rpm_result is successful + +# UNINSTALL +# With 'python2-dnf' uninstalled, the first call to 'dnf' should install +# python2-dnf. +- name: uninstall sos + dnf: + name: sos + state: removed + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_result + +- name: verify uninstallation of sos + assert: + that: + - "not dnf_result.failed | default(False)" + - "rpm_result.rc == 1" + +# UNINSTALL AGAIN +- name: uninstall sos + dnf: + name: sos + state: removed + register: dnf_result + +- name: verify no change on re-uninstall + assert: + that: + - "not dnf_result.changed" + +# INSTALL +- name: install sos (check_mode) + dnf: + name: sos + state: present + update_cache: True + check_mode: True + register: dnf_result + +- assert: + that: + - dnf_result is success + - dnf_result.results|length > 0 + - "dnf_result.results[0].startswith('Installed: ')" + +- name: install sos + dnf: + name: sos + state: present + update_cache: True + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_result + +- name: verify installation of sos + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_result.rc == 0" + +- name: verify dnf module outputs + assert: + that: + - "'changed' in dnf_result" + - "'results' in dnf_result" + +# INSTALL AGAIN +- name: install sos again (check_mode) + dnf: + name: sos + state: present + check_mode: True + register: dnf_result + +- assert: + that: + - dnf_result is not changed + - dnf_result.results|length == 0 + +- name: install sos again + dnf: + name: sos + state: present + register: dnf_result + +- name: verify no change on second install + assert: + that: + - "not dnf_result.changed" + +# Multiple packages +- name: uninstall sos and dos2unix + dnf: name=sos,dos2unix state=removed + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check dos2unix with rpm + shell: rpm -q dos2unix + failed_when: False + register: rpm_dos2unix_result + +- name: verify packages installed + assert: + that: + - "rpm_sos_result.rc != 0" + - "rpm_dos2unix_result.rc != 0" + +- name: install sos and dos2unix as comma separated + dnf: name=sos,dos2unix state=present + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check dos2unix with rpm + shell: rpm -q dos2unix + failed_when: False + register: rpm_dos2unix_result + +- name: verify packages installed + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" + +- name: uninstall sos and dos2unix + dnf: name=sos,dos2unix state=removed + register: dnf_result + +- name: install sos and dos2unix as list + dnf: + name: + - sos + - dos2unix + state: present + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check dos2unix with rpm + shell: rpm -q dos2unix + failed_when: False + register: rpm_dos2unix_result + +- name: verify packages installed + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" + +- name: uninstall sos and dos2unix + dnf: + name: "sos,dos2unix" + state: removed + register: dnf_result + +- name: install sos and dos2unix as comma separated with spaces + dnf: + name: "sos, dos2unix" + state: present + register: dnf_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check sos with rpm + shell: rpm -q dos2unix + failed_when: False + register: rpm_dos2unix_result + +- name: verify packages installed + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_dos2unix_result.rc == 0" + +- name: uninstall sos and dos2unix (check_mode) + dnf: + name: + - sos + - dos2unix + state: removed + check_mode: True + register: dnf_result + +- assert: + that: + - dnf_result is success + - dnf_result.results|length == 2 + - "dnf_result.results[0].startswith('Removed: ')" + - "dnf_result.results[1].startswith('Removed: ')" + +- name: uninstall sos and dos2unix + dnf: + name: + - sos + - dos2unix + state: removed + register: dnf_result + +- assert: + that: + - dnf_result is changed + +- name: install non-existent rpm + dnf: + name: does-not-exist + register: non_existent_rpm + ignore_errors: True + +- name: check non-existent rpm install failed + assert: + that: + - non_existent_rpm is failed + +# Install in installroot='/'. This should be identical to default +- name: install sos in / + dnf: name=sos state=present installroot='/' + register: dnf_result + +- name: check sos with rpm in / + shell: rpm -q sos --root=/ + failed_when: False + register: rpm_result + +- name: verify installation of sos in / + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_result.rc == 0" + +- name: verify dnf module outputs in / + assert: + that: + - "'changed' in dnf_result" + - "'results' in dnf_result" + +- name: uninstall sos in / + dnf: name=sos installroot='/' + register: dnf_result + +- name: uninstall sos for downloadonly test + dnf: + name: sos + state: absent + +- name: Test download_only (check_mode) + dnf: + name: sos + state: latest + download_only: true + check_mode: true + register: dnf_result + +- assert: + that: + - dnf_result is success + - "dnf_result.results[0].startswith('Downloaded: ')" + +- name: Test download_only + dnf: + name: sos + state: latest + download_only: true + register: dnf_result + +- name: verify download of sos (part 1 -- dnf "install" succeeded) + assert: + that: + - "dnf_result is success" + - "dnf_result is changed" + +- name: uninstall sos (noop) + dnf: + name: sos + state: absent + register: dnf_result + +- name: verify download of sos (part 2 -- nothing removed during uninstall) + assert: + that: + - "dnf_result is success" + - "not dnf_result is changed" + +- name: uninstall sos for downloadonly/downloaddir test + dnf: + name: sos + state: absent + +- name: Test download_only/download_dir + dnf: + name: sos + state: latest + download_only: true + download_dir: "/var/tmp/packages" + register: dnf_result + +- name: verify dnf output + assert: + that: + - "dnf_result is success" + - "dnf_result is changed" + +- command: "ls /var/tmp/packages" + register: ls_out + +- name: Verify specified download_dir was used + assert: + that: + - "'sos' in ls_out.stdout" + +# GROUP INSTALL +- name: install Custom Group group + dnf: + name: "@Custom Group" + state: present + register: dnf_result + +- name: check dinginessentail with rpm + command: rpm -q dinginessentail + failed_when: False + register: dinginessentail_result + +- name: verify installation of the group + assert: + that: + - not dnf_result is failed + - dnf_result is changed + - "'results' in dnf_result" + - dinginessentail_result.rc == 0 + +- name: install the group again + dnf: + name: "@Custom Group" + state: present + register: dnf_result + +- name: verify nothing changed + assert: + that: + - not dnf_result is changed + - "'msg' in dnf_result" + +- name: verify that landsidescalping is not installed + dnf: + name: landsidescalping + state: absent + +- name: install the group again but also with a package that is not yet installed + dnf: + name: + - "@Custom Group" + - landsidescalping + state: present + register: dnf_result + +- name: check landsidescalping with rpm + command: rpm -q landsidescalping + failed_when: False + register: landsidescalping_result + +- name: verify landsidescalping is installed + assert: + that: + - dnf_result is changed + - "'results' in dnf_result" + - landsidescalping_result.rc == 0 + +- name: try to install the group again, with --check to check 'changed' + dnf: + name: "@Custom Group" + state: present + check_mode: yes + register: dnf_result + +- name: verify nothing changed + assert: + that: + - not dnf_result is changed + - "'msg' in dnf_result" + +- name: remove landsidescalping after test + dnf: + name: landsidescalping + state: absent + +# cleanup until https://github.com/ansible/ansible/issues/27377 is resolved +- shell: 'dnf -y group install "Custom Group" && dnf -y group remove "Custom Group"' + register: shell_dnf_result + +# GROUP UPGRADE - this will go to the same method as group install +# but through group_update - it is its invocation we're testing here +# see commit 119c9e5d6eb572c4a4800fbe8136095f9063c37b +- name: install latest Custom Group + dnf: + name: "@Custom Group" + state: latest + register: dnf_result + +- name: verify installation of the group + assert: + that: + - not dnf_result is failed + - dnf_result is changed + - "'results' in dnf_result" + +# cleanup until https://github.com/ansible/ansible/issues/27377 is resolved +- shell: dnf -y group install "Custom Group" && dnf -y group remove "Custom Group" + +- name: try to install non existing group + dnf: + name: "@non-existing-group" + state: present + register: dnf_result + ignore_errors: True + +- name: verify installation of the non existing group failed + assert: + that: + - "not dnf_result.changed" + - "dnf_result is failed" + +- name: verify dnf module outputs + assert: + that: + - "'changed' in dnf_result" + - "'msg' in dnf_result" + +- name: try to install non existing file + dnf: + name: /tmp/non-existing-1.0.0.fc26.noarch.rpm + state: present + register: dnf_result + ignore_errors: yes + +- name: verify installation failed + assert: + that: + - "dnf_result is failed" + - "not dnf_result.changed" + +- name: verify dnf module outputs + assert: + that: + - "'changed' in dnf_result" + - "'msg' in dnf_result" + +- name: try to install from non existing url + dnf: + name: https://ci-files.testing.ansible.com/test/integration/targets/dnf/non-existing-1.0.0.fc26.noarch.rpm + state: present + register: dnf_result + ignore_errors: yes + +- name: verify installation failed + assert: + that: + - "dnf_result is failed" + - "not dnf_result.changed" + +- name: verify dnf module outputs + assert: + that: + - "'changed' in dnf_result" + - "'msg' in dnf_result" + +# ENVIRONMENT UPGRADE +# see commit de299ef77c03a64a8f515033a79ac6b7db1bc710 +- name: install Custom Environment Group + dnf: + name: "@Custom Environment Group" + state: latest + register: dnf_result + +- name: check landsidescalping with rpm + command: rpm -q landsidescalping + register: landsidescalping_result + +- name: verify installation of the environment + assert: + that: + - not dnf_result is failed + - dnf_result is changed + - "'results' in dnf_result" + - landsidescalping_result.rc == 0 + +# Fedora 28 (DNF 2) does not support this, just remove the package itself +- name: remove landsidescalping package on Fedora 28 + dnf: + name: landsidescalping + state: absent + when: ansible_distribution == 'Fedora' and ansible_distribution_major_version|int <= 28 + +# cleanup until https://github.com/ansible/ansible/issues/27377 is resolved +- name: remove Custom Environment Group + shell: dnf -y group install "Custom Environment Group" && dnf -y group remove "Custom Environment Group" + when: not (ansible_distribution == 'Fedora' and ansible_distribution_major_version|int <= 28) + +# https://github.com/ansible/ansible/issues/39704 +- name: install non-existent rpm, state=latest + dnf: + name: non-existent-rpm + state: latest + ignore_errors: yes + register: dnf_result + +- name: verify the result + assert: + that: + - "dnf_result is failed" + - "'non-existent-rpm' in dnf_result['failures'][0]" + - "'No package non-existent-rpm available' in dnf_result['failures'][0]" + - "'Failed to install some of the specified packages' in dnf_result['msg']" + +- name: use latest to install httpd + dnf: + name: httpd + state: latest + register: dnf_result + +- name: verify httpd was installed + assert: + that: + - "'changed' in dnf_result" + +- name: uninstall httpd + dnf: + name: httpd + state: removed + +- name: update httpd only if it exists + dnf: + name: httpd + state: latest + update_only: yes + register: dnf_result + +- name: verify httpd not installed + assert: + that: + - "not dnf_result is changed" + +- name: try to install not compatible arch rpm, should fail + dnf: + name: https://ci-files.testing.ansible.com/test/integration/targets/dnf/banner-1.3.4-3.el7.ppc64le.rpm + state: present + register: dnf_result + ignore_errors: True + +- name: verify that dnf failed + assert: + that: + - "not dnf_result is changed" + - "dnf_result is failed" + +# setup for testing installing an RPM from local file + +- set_fact: + pkg_name: noarchfake + pkg_path: '{{ repodir }}/noarchfake-1.0-1.noarch.rpm' + +- name: cleanup + dnf: + name: "{{ pkg_name }}" + state: absent + +# setup end + +- name: install a local noarch rpm from file + dnf: + name: "{{ pkg_path }}" + state: present + disable_gpg_check: true + register: dnf_result + +- name: verify installation + assert: + that: + - "dnf_result is success" + - "dnf_result is changed" + +- name: install the downloaded rpm again + dnf: + name: "{{ pkg_path }}" + state: present + register: dnf_result + +- name: verify installation + assert: + that: + - "dnf_result is success" + - "not dnf_result is changed" + +- name: clean up + dnf: + name: "{{ pkg_name }}" + state: absent + +- name: install from url + dnf: + name: "file://{{ pkg_path }}" + state: present + disable_gpg_check: true + register: dnf_result + +- name: verify installation + assert: + that: + - "dnf_result is success" + - "dnf_result is changed" + - "dnf_result is not failed" + +- name: verify dnf module outputs + assert: + that: + - "'changed' in dnf_result" + - "'results' in dnf_result" + +- name: Create a temp RPM file which does not contain nevra information + file: + name: "/tmp/non_existent_pkg.rpm" + state: touch + +- name: Try installing RPM file which does not contain nevra information + dnf: + name: "/tmp/non_existent_pkg.rpm" + state: present + register: no_nevra_info_result + ignore_errors: yes + +- name: Verify RPM failed to install + assert: + that: + - "'changed' in no_nevra_info_result" + - "'msg' in no_nevra_info_result" + +- name: Delete a temp RPM file + file: + name: "/tmp/non_existent_pkg.rpm" + state: absent + +- name: uninstall lsof + dnf: + name: lsof + state: removed + +- name: check lsof with rpm + shell: rpm -q lsof + ignore_errors: True + register: rpm_lsof_result + +- name: verify lsof is uninstalled + assert: + that: + - "rpm_lsof_result is failed" + +- name: create conf file that excludes lsof + copy: + content: | + [main] + exclude=lsof* + dest: '{{ remote_tmp_dir }}/test-dnf.conf' + register: test_dnf_copy + +- block: + # begin test case where disable_excludes is supported + - name: Try install lsof without disable_excludes + dnf: name=lsof state=latest conf_file={{ test_dnf_copy.dest }} + register: dnf_lsof_result + ignore_errors: True + + - name: verify lsof did not install because it is in exclude list + assert: + that: + - "dnf_lsof_result is failed" + + - name: install lsof with disable_excludes + dnf: name=lsof state=latest disable_excludes=all conf_file={{ test_dnf_copy.dest }} + register: dnf_lsof_result_using_excludes + + - name: verify lsof did install using disable_excludes=all + assert: + that: + - "dnf_lsof_result_using_excludes is success" + - "dnf_lsof_result_using_excludes is changed" + - "dnf_lsof_result_using_excludes is not failed" + always: + - name: remove exclude lsof conf file + file: + path: '{{ remote_tmp_dir }}/test-dnf.conf' + state: absent + +# end test case where disable_excludes is supported + +- name: Test "dnf install /usr/bin/vi" + block: + - name: Clean vim-minimal + dnf: + name: vim-minimal + state: absent + + - name: Install vim-minimal by specifying "/usr/bin/vi" + dnf: + name: /usr/bin/vi + state: present + + - name: Get rpm output + command: rpm -q vim-minimal + register: rpm_output + + - name: Check installation was successful + assert: + that: + - "'vim-minimal' in rpm_output.stdout" + when: + - ansible_distribution == 'Fedora' + +- name: Remove wildcard package that isn't installed + dnf: + name: firefox* + state: absent + register: wildcard_absent + +- assert: + that: + - wildcard_absent is successful + - wildcard_absent is not changed + +- name: Test removing with various package specs + block: + - name: Ensure sos is installed + dnf: + name: sos + state: present + + - name: Determine version of sos + command: rpm -q --queryformat=%{version} sos + register: sos_version_command + + - name: Determine release of sos + command: rpm -q --queryformat=%{release} sos + register: sos_release_command + + - name: Determine arch of sos + command: rpm -q --queryformat=%{arch} sos + register: sos_arch_command + + - set_fact: + sos_version: "{{ sos_version_command.stdout | trim }}" + sos_release: "{{ sos_release_command.stdout | trim }}" + sos_arch: "{{ sos_arch_command.stdout | trim }}" + + # We are just trying to remove the package by specifying its spec in a + # bunch of different ways. Each "item" here is a test (a name passed, to make + # sure it matches, with how we call Hawkey in the dnf module). + - include_tasks: test_sos_removal.yml + with_items: + - sos + - sos-{{ sos_version }} + - sos-{{ sos_version }}-{{ sos_release }} + - sos-{{ sos_version }}-{{ sos_release }}.{{ sos_arch }} + - sos-*-{{ sos_release }} + - sos-{{ sos_version[0] }}* + - sos-{{ sos_version[0] }}*-{{ sos_release }} + - sos-{{ sos_version[0] }}*{{ sos_arch }} + + - name: Ensure deleting a non-existing package fails correctly + dnf: + name: a-non-existent-package + state: absent + ignore_errors: true + register: nonexisting + + - assert: + that: + - nonexisting is success + - nonexisting.msg == 'Nothing to do' + +# running on RHEL which is --remote where .mo language files are present +# for dnf as opposed to in --docker +- when: ansible_distribution == 'RedHat' + block: + - dnf: + name: langpacks-ja + state: present + + - dnf: + name: nginx-mod* + state: absent + environment: + LANG: ja_JP.UTF-8 + always: + - dnf: + name: langpacks-ja + state: absent diff --git a/test/integration/targets/dnf/tasks/dnfinstallroot.yml b/test/integration/targets/dnf/tasks/dnfinstallroot.yml new file mode 100644 index 0000000..19f6706 --- /dev/null +++ b/test/integration/targets/dnf/tasks/dnfinstallroot.yml @@ -0,0 +1,35 @@ +# make a installroot +- name: Create installroot + command: mktemp -d "{{ remote_tmp_dir }}/ansible.test.XXXXXX" + register: dnfroot + +# This will drag in > 200 MB. +- name: attempt installroot + dnf: name=sos installroot="/{{ dnfroot.stdout }}/" disable_gpg_check=yes releasever={{ansible_facts['distribution_major_version']}} + register: dnf_result + +- name: check sos with rpm in installroot + shell: rpm -q sos --root="/{{ dnfroot.stdout }}/" + failed_when: False + register: rpm_result + +- debug: var=dnf_result +- debug: var=rpm_result + +- name: verify installation of sos in installroot + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_result.rc == 0" + +- name: verify dnf module outputs in / + assert: + that: + - "'changed' in dnf_result" + - "'results' in dnf_result" + +- name: cleanup installroot + file: + path: "/{{ dnfroot.stdout }}/" + state: absent diff --git a/test/integration/targets/dnf/tasks/dnfreleasever.yml b/test/integration/targets/dnf/tasks/dnfreleasever.yml new file mode 100644 index 0000000..351a26b --- /dev/null +++ b/test/integration/targets/dnf/tasks/dnfreleasever.yml @@ -0,0 +1,47 @@ +# make an installroot +- name: Create installroot + command: mktemp -d "{{ remote_tmp_dir }}/ansible.test.XXXXXX" + register: dnfroot + +- name: Make a necessary directory + file: + path: "/{{dnfroot.stdout}}/etc/dnf/vars" + state: directory + mode: 0755 + +- name: Populate directory + copy: + content: "{{ansible_distribution_version}}\n" + dest: "/{{dnfroot.stdout}}/etc/dnf/vars/releasever" + +- name: attempt releasever to the installroot + dnf: + name: filesystem + installroot: '/{{dnfroot.stdout}}' + releasever: '{{ansible_distribution_version|int - 1}}' + register: dnf_result + +- name: check filesystem version + shell: rpm -q filesystem --root="/{{dnfroot.stdout}}/" + failed_when: False + register: rpm_result + +- debug: var=dnf_result +- debug: var=rpm_result + +- name: verify installation was done + assert: + that: + - "not dnf_result.failed | default(False)" + - "dnf_result.changed" + - "rpm_result.rc == 0" + +- name: verify the version + assert: + that: + - "rpm_result.stdout.find('fc' ~ (ansible_distribution_version|int - 1)) != -1" + +- name: cleanup installroot + file: + path: "/{{dnfroot.stdout}}/" + state: absent diff --git a/test/integration/targets/dnf/tasks/filters.yml b/test/integration/targets/dnf/tasks/filters.yml new file mode 100644 index 0000000..1ce9b66 --- /dev/null +++ b/test/integration/targets/dnf/tasks/filters.yml @@ -0,0 +1,162 @@ +# We have a test repo set up with a valid updateinfo.xml which is referenced +# from its repomd.xml. +- block: + - set_fact: + updateinfo_repo: https://ci-files.testing.ansible.com/test/integration/targets/setup_rpm_repo/repo-with-updateinfo + + - name: Install the test repo + yum_repository: + name: test-repo-with-updateinfo + description: test-repo-with-updateinfo + baseurl: "{{ updateinfo_repo }}" + gpgcheck: no + + - name: Install old versions of toaster and oven + dnf: + name: + - "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + - "{{ updateinfo_repo }}/oven-1.2.3.4-1.el8.noarch.rpm" + disable_gpg_check: true + + - name: Ask for pending updates + dnf: + name: '*' + state: latest + update_only: true + disable_gpg_check: true + disablerepo: '*' + enablerepo: test-repo-with-updateinfo + register: update_no_filter + + - assert: + that: + - update_no_filter is changed + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_no_filter.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_no_filter.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_no_filter.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_no_filter.results' + + - name: Install old versions of toaster and oven + dnf: + name: + - "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + - "{{ updateinfo_repo }}/oven-1.2.3.4-1.el8.noarch.rpm" + allow_downgrade: true + disable_gpg_check: true + + - name: Ask for pending updates with security=true + dnf: + name: '*' + state: latest + update_only: true + disable_gpg_check: true + security: true + disablerepo: '*' + enablerepo: test-repo-with-updateinfo + register: update_security + + - assert: + that: + - update_security is changed + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_security.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_security.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" not in update_security.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" not in update_security.results' + + - name: Install old versions of toaster and oven + dnf: + name: + - "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + - "{{ updateinfo_repo }}/oven-1.2.3.4-1.el8.noarch.rpm" + allow_downgrade: true + disable_gpg_check: true + + - name: Ask for pending updates with bugfix=true + dnf: + name: '*' + state: latest + update_only: true + disable_gpg_check: true + bugfix: true + disablerepo: '*' + enablerepo: test-repo-with-updateinfo + register: update_bugfix + + - assert: + that: + - update_bugfix is changed + - '"Installed: toaster-1.2.3.5-1.el8.noarch" not in update_bugfix.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" not in update_bugfix.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_bugfix.results' + + - name: Install old versions of toaster and oven + dnf: + name: + - "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + - "{{ updateinfo_repo }}/oven-1.2.3.4-1.el8.noarch.rpm" + allow_downgrade: true + disable_gpg_check: true + + - name: Verify toaster is not upgraded with state=installed + dnf: + name: "{{ item }}" + state: installed + register: installed + loop: + - toaster + - toaster.noarch + - toaster-1.2.3.4-1.el8.noarch + + - assert: + that: "installed.results | map(attribute='changed') is not any" + + - name: Ask for pending updates with bugfix=true and security=true + dnf: + name: '*' + state: latest + update_only: true + disable_gpg_check: true + bugfix: true + security: true + disablerepo: '*' + enablerepo: test-repo-with-updateinfo + register: update_bugfix + + - assert: + that: + - update_bugfix is changed + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_bugfix.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_bugfix.results' + + - name: Install old version of toaster again + dnf: + name: "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + allow_downgrade: true + disable_gpg_check: true + + - name: Verify toaster is upgraded with state=latest + dnf: + name: toaster.noarch + state: latest + register: result + + - assert: + that: result.changed + + always: + - name: Remove installed packages + dnf: + name: + - toaster + - oven + state: absent + + - name: Remove the repo + yum_repository: + name: test-repo-with-updateinfo + state: absent + tags: + - filters diff --git a/test/integration/targets/dnf/tasks/filters_check_mode.yml b/test/integration/targets/dnf/tasks/filters_check_mode.yml new file mode 100644 index 0000000..c931c07 --- /dev/null +++ b/test/integration/targets/dnf/tasks/filters_check_mode.yml @@ -0,0 +1,118 @@ +# We have a test repo set up with a valid updateinfo.xml which is referenced +# from its repomd.xml. +- block: + - set_fact: + updateinfo_repo: https://ci-files.testing.ansible.com/test/integration/targets/setup_rpm_repo/repo-with-updateinfo + + - name: Install the test repo + yum_repository: + name: test-repo-with-updateinfo + description: test-repo-with-updateinfo + baseurl: "{{ updateinfo_repo }}" + gpgcheck: no + + - name: Install old versions of toaster and oven + dnf: + name: + - "{{ updateinfo_repo }}/toaster-1.2.3.4-1.el8.noarch.rpm" + - "{{ updateinfo_repo }}/oven-1.2.3.4-1.el8.noarch.rpm" + disable_gpg_check: true + + - name: Ask for pending updates (check_mode) + dnf: + name: + - toaster + - oven + state: latest + update_only: true + disable_gpg_check: true + check_mode: true + register: update_no_filter + + - assert: + that: + - update_no_filter is changed + - '"would have if not in check mode" in update_no_filter.msg' + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_no_filter.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_no_filter.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_no_filter.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_no_filter.results' + + - name: Ask for pending updates with security=true (check_mode) + dnf: + name: + - toaster + - oven + state: latest + update_only: true + disable_gpg_check: true + security: true + check_mode: true + register: update_security + + - assert: + that: + - update_security is changed + - '"would have if not in check mode" in update_security.msg' + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_security.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_security.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" not in update_security.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" not in update_security.results' + + - name: Ask for pending updates with bugfix=true (check_mode) + dnf: + name: + - toaster + - oven + state: latest + update_only: true + disable_gpg_check: true + bugfix: true + check_mode: true + register: update_bugfix + + - assert: + that: + - update_bugfix is changed + - '"would have if not in check mode" in update_bugfix.msg' + - '"Installed: toaster-1.2.3.5-1.el8.noarch" not in update_bugfix.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" not in update_bugfix.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_bugfix.results' + + - name: Ask for pending updates with bugfix=true and security=true (check_mode) + dnf: + name: + - toaster + - oven + state: latest + update_only: true + disable_gpg_check: true + bugfix: true + security: true + check_mode: true + register: update_bugfix + + - assert: + that: + - update_bugfix is changed + - '"would have if not in check mode" in update_bugfix.msg' + - '"Installed: toaster-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: toaster-1.2.3.4-1.el8.noarch" in update_bugfix.results' + - '"Installed: oven-1.2.3.5-1.el8.noarch" in update_bugfix.results' + - '"Removed: oven-1.2.3.4-1.el8.noarch" in update_bugfix.results' + + always: + - name: Remove installed packages + dnf: + name: + - toaster + - oven + state: absent + + - name: Remove the repo + yum_repository: + name: test-repo-with-updateinfo + state: absent + tags: + - filters diff --git a/test/integration/targets/dnf/tasks/gpg.yml b/test/integration/targets/dnf/tasks/gpg.yml new file mode 100644 index 0000000..72bdee0 --- /dev/null +++ b/test/integration/targets/dnf/tasks/gpg.yml @@ -0,0 +1,88 @@ +# Set up a repo of unsigned rpms +- block: + - set_fact: + pkg_name: langtable + pkg_repo_dir: "{{ remote_tmp_dir }}/unsigned" + + - name: Ensure our test package isn't already installed + dnf: + name: + - '{{ pkg_name }}' + state: absent + + - name: Install rpm-sign and attr + dnf: + name: + - rpm-sign + - attr + state: present + + - name: Create directory to use as local repo + file: + path: "{{ pkg_repo_dir }}" + state: directory + + - name: Download the test package + dnf: + name: '{{ pkg_name }}' + state: latest + download_only: true + download_dir: "{{ pkg_repo_dir }}" + + - name: Unsign the RPM + shell: rpmsign --delsign {{ remote_tmp_dir }}/unsigned/{{ pkg_name }}* + + # In RHEL 8.5 dnf uses libdnf to do checksum verification, which caches the checksum on an xattr of the file + # itself, so we need to clear that cache + - name: Clear libdnf checksum cache + shell: setfattr -x user.Librepo.checksum.sha256 {{ remote_tmp_dir }}/unsigned/{{ pkg_name }}* + when: ansible_distribution in ['RedHat', 'CentOS'] and + ansible_distribution_version is version('8.5', '>=') and + ansible_distribution_version is version('9', '<') + + - name: createrepo + command: createrepo . + args: + chdir: "{{ pkg_repo_dir }}" + + - name: Add the repo + yum_repository: + name: unsigned + description: unsigned rpms + baseurl: "file://{{ pkg_repo_dir }}" + # we want to ensure that signing is verified + gpgcheck: true + + - name: Install test package + dnf: + name: + - "{{ pkg_name }}" + disablerepo: '*' + enablerepo: unsigned + register: res + ignore_errors: yes + + - assert: + that: + - res is failed + - "'Failed to validate GPG signature' in res.msg" + - "'is not signed' in res.msg" + + always: + - name: Remove rpm-sign and attr (and test package if it got installed) + dnf: + name: + - rpm-sign + - attr + - "{{ pkg_name }}" + state: absent + + - name: Remove test repo + yum_repository: + name: unsigned + state: absent + + - name: Remove repo dir + file: + path: "{{ pkg_repo_dir }}" + state: absent diff --git a/test/integration/targets/dnf/tasks/logging.yml b/test/integration/targets/dnf/tasks/logging.yml new file mode 100644 index 0000000..903bf56 --- /dev/null +++ b/test/integration/targets/dnf/tasks/logging.yml @@ -0,0 +1,48 @@ +# Verify logging function is enabled in the dnf module. +# The following tasks has been supported in dnf-4.2.17-6 or later +# Note: https://bugzilla.redhat.com/show_bug.cgi?id=1788212 +- name: Install latest version python3-dnf + dnf: + name: + - python3-dnf + - python3-libdnf # https://bugzilla.redhat.com/show_bug.cgi?id=1887502 + - libmodulemd # https://bugzilla.redhat.com/show_bug.cgi?id=1942236 + state: latest + register: dnf_result + +- name: Verify python3-dnf installed + assert: + that: + - "dnf_result.rc == 0" + +- name: Get python3-dnf version + shell: "dnf info python3-dnf | awk '/^Version/ { print $3 }'" + register: py3_dnf_version + +- name: Check logging enabled + block: + - name: remove logfiles if exist + file: + path: "{{ item }}" + state: absent + loop: "{{ dnf_log_files }}" + + - name: Install sos package + dnf: + name: sos + state: present + register: dnf_result + + - name: Get status of logfiles + stat: + path: "{{ item }}" + loop: "{{ dnf_log_files }}" + register: stats + + - name: Verify logfile exists + assert: + that: + - "item.stat.exists" + loop: "{{ stats.results }}" + when: + - 'py3_dnf_version.stdout is version("4.2.17", ">=")' diff --git a/test/integration/targets/dnf/tasks/main.yml b/test/integration/targets/dnf/tasks/main.yml new file mode 100644 index 0000000..66a171a --- /dev/null +++ b/test/integration/targets/dnf/tasks/main.yml @@ -0,0 +1,74 @@ +# test code for the dnf module +# (c) 2014, James Tanner <tanner.jc@gmail.com> + +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Note: We install the yum package onto Fedora so that this will work on dnf systems +# We want to test that for people who don't want to upgrade their systems. + +- include_tasks: dnf.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +- include_tasks: skip_broken_and_nobest.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +- include_tasks: filters_check_mode.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + tags: + - filters + +- include_tasks: filters.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + tags: + - filters + +- include_tasks: gpg.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +- include_tasks: repo.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +- include_tasks: dnfinstallroot.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +# Attempting to install a different RHEL release in a tmpdir doesn't work (rhel8 beta) +- include_tasks: dnfreleasever.yml + when: + - ansible_distribution == 'Fedora' + - ansible_distribution_major_version is version('23', '>=') + +- include_tasks: modularity.yml + when: + - astream_name is defined + - (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('29', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + tags: + - dnf_modularity + +- include_tasks: logging.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('31', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) + +- include_tasks: cacheonly.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) diff --git a/test/integration/targets/dnf/tasks/modularity.yml b/test/integration/targets/dnf/tasks/modularity.yml new file mode 100644 index 0000000..94f43a4 --- /dev/null +++ b/test/integration/targets/dnf/tasks/modularity.yml @@ -0,0 +1,104 @@ +# FUTURE - look at including AppStream support in our local repo +- name: Include distribution specific variables + include_vars: "{{ item }}" + with_first_found: + - files: + - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" + - "{{ ansible_facts.distribution }}.yml" + paths: ../vars + +- name: install "{{ astream_name }}" module + dnf: + name: "{{ astream_name }}" + state: present + register: dnf_result + +- name: verify installation of "{{ astream_name }}" module + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: install "{{ astream_name }}" module again + dnf: + name: "{{ astream_name }}" + state: present + register: dnf_result + +- name: verify installation of "{{ astream_name }}" module again + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed" + +- name: uninstall "{{ astream_name }}" module + dnf: + name: "{{ astream_name }}" + state: absent + register: dnf_result + +- name: verify uninstallation of "{{ astream_name }}" module + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: uninstall "{{ astream_name }}" module again + dnf: + name: "{{ astream_name }}" + state: absent + register: dnf_result + +- name: verify uninstallation of "{{ astream_name }}" module again + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed" + +- name: install "{{ astream_name_no_stream }}" module without providing stream + dnf: + name: "{{ astream_name_no_stream }}" + state: present + register: dnf_result + +- name: verify installation of "{{ astream_name_no_stream }}" module without providing stream + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: install "{{ astream_name_no_stream }}" module again without providing stream + dnf: + name: "{{ astream_name_no_stream }}" + state: present + register: dnf_result + +- name: verify installation of "{{ astream_name_no_stream }}" module again without providing stream + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed" + +- name: uninstall "{{ astream_name_no_stream }}" module without providing stream + dnf: + name: "{{ astream_name_no_stream }}" + state: absent + register: dnf_result + +- name: verify uninstallation of "{{ astream_name_no_stream }}" module without providing stream + assert: + that: + - "not dnf_result.failed" + - "dnf_result.changed" + +- name: uninstall "{{ astream_name_no_stream }}" module again without providing stream + dnf: + name: "{{ astream_name_no_stream }}" + state: absent + register: dnf_result + +- name: verify uninstallation of "{{ astream_name_no_stream }}" module again without providing stream + assert: + that: + - "not dnf_result.failed" + - "not dnf_result.changed" diff --git a/test/integration/targets/dnf/tasks/repo.yml b/test/integration/targets/dnf/tasks/repo.yml new file mode 100644 index 0000000..4f82899 --- /dev/null +++ b/test/integration/targets/dnf/tasks/repo.yml @@ -0,0 +1,309 @@ +- block: + - name: Install dinginessentail-1.0-1 + dnf: + name: dinginessentail-1.0-1 + state: present + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + # ============================================================================ + - name: Install dinginessentail-1.0-1 again + dnf: + name: dinginessentail-1.0-1 + state: present + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "not dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + + - name: Verify dnf module outputs + assert: + that: + - "'msg' in dnf_result" + # ============================================================================ + - name: Install dinginessentail again (noop, module is idempotent) + dnf: + name: dinginessentail + state: present + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + # No upgrade happened to 1.1.1 + - "not dnf_result.changed" + # Old version still installed + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + # ============================================================================ + - name: Install dinginessentail-1:1.0-2 + dnf: + name: "dinginessentail-1:1.0-2.{{ ansible_architecture }}" + state: present + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + # ============================================================================ + - name: Update to the latest dinginessentail + dnf: + name: dinginessentail + state: latest + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.1-1')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + # ============================================================================ + - name: Install dinginessentail-1.0-1 from a file (downgrade) + dnf: + name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" + state: present + allow_downgrade: True + disable_gpg_check: True + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + + - name: Remove dinginessentail + dnf: + name: dinginessentail + state: absent + # ============================================================================ + - name: Install dinginessentail-1.0-1 from a file + dnf: + name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" + state: present + disable_gpg_check: True + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + # ============================================================================ + - name: Install dinginessentail-1.0-1 from a file again + dnf: + name: "{{ repodir }}/dinginessentail-1.0-1.{{ ansible_architecture }}.rpm" + state: present + disable_gpg_check: True + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "not dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-1')" + # ============================================================================ + - name: Install dinginessentail-1.0-2 from a file + dnf: + name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" + state: present + disable_gpg_check: True + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" + + - name: Verify dnf module outputs + assert: + that: + - "'results' in dnf_result" + # ============================================================================ + - name: Install dinginessentail-1.0-2 from a file again + dnf: + name: "{{ repodir }}/dinginessentail-1.0-2.{{ ansible_architecture }}.rpm" + state: present + disable_gpg_check: True + register: dnf_result + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + + - name: Verify installation + assert: + that: + - "not dnf_result.changed" + - "rpm_result.stdout.startswith('dinginessentail-1.0-2')" + # ============================================================================ + - name: Remove dinginessentail + dnf: + name: dinginessentail + state: absent + + - name: Try to install incompatible arch + dnf: + name: "{{ repodir_ppc64 }}/dinginessentail-1.0-1.ppc64.rpm" + state: present + register: dnf_result + ignore_errors: yes + + - name: Check dinginessentail with rpm + shell: rpm -q dinginessentail + register: rpm_result + ignore_errors: yes + + - name: Verify installation + assert: + that: + - "rpm_result.rc == 1" + - "not dnf_result.changed" + - "dnf_result is failed" + # ============================================================================ + + # Should install dinginessentail-with-weak-dep and dinginessentail-weak-dep + - name: Install package with defaults + dnf: + name: dinginessentail-with-weak-dep + state: present + + - name: Check if dinginessentail-with-weak-dep is installed + shell: rpm -q dinginessentail-with-weak-dep + register: rpm_main_result + + - name: Check if dinginessentail-weak-dep is installed + shell: rpm -q dinginessentail-weak-dep + register: rpm_weak_result + + - name: Verify install with weak deps + assert: + that: + - rpm_main_result.rc == 0 + - rpm_weak_result.rc == 0 + + - name: Uninstall dinginessentail weak dep packages + dnf: + name: + - dinginessentail-with-weak-dep + - dinginessentail-weak-dep + state: absent + + - name: Install package with weak deps but skip weak deps + dnf: + name: dinginessentail-with-weak-dep + install_weak_deps: False + state: present + + - name: Check if dinginessentail-with-weak-dep is installed + shell: rpm -q dinginessentail-with-weak-dep + register: rpm_main_result + + - name: Check if dinginessentail-weak-dep is installed + shell: rpm -q dinginessentail-weak-dep + register: rpm_weak_result + ignore_errors: yes + + - name: Verify install without weak deps + assert: + that: + - rpm_main_result.rc == 0 + - rpm_weak_result.rc == 1 # the weak dependency shouldn't be installed + + # https://github.com/ansible/ansible/issues/55938 + - name: Install dinginessentail-* + dnf: + name: dinginessentail-* + state: present + + - name: Uninstall dinginessentail-* + dnf: + name: dinginessentail-* + state: absent + + - name: Check if all dinginessentail packages are removed + shell: rpm -qa dinginessentail-* | wc -l + register: rpm_result + + - name: Verify rpm result + assert: + that: + - rpm_result.stdout == '0' + always: + - name: Clean up + dnf: + name: + - dinginessentail + - dinginessentail-with-weak-dep + - dinginessentail-weak-dep + state: absent diff --git a/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml new file mode 100644 index 0000000..503cb4c --- /dev/null +++ b/test/integration/targets/dnf/tasks/skip_broken_and_nobest.yml @@ -0,0 +1,318 @@ +# Tests for skip_broken and allowerasing +# (and a bit of nobest because the test case is too good to pass up) +# +# There are a lot of fairly complex, corner cases we test here especially towards the end. +# +# The test repo is generated from the "skip-broken" repo in this repository: +# https://github.com/relrod/ansible-ci-contrived-yum-repos +# +# It is laid out like this: +# +# There are three packages, `broken-a`, `broken-b`, `broken-c`. +# +# * broken-a has three versions: 1.2.3, 1.2.3.4, 1.2.4, 2.0.0. +# * 1.2.3 and 1.2.4 have no dependencies +# * 1.2.3.4 and 2.0.0 both depend on a non-existent package (to break depsolving) +# +# * broken-b depends on broken-a-1.2.3 +# * broken-c depends on broken-a-1.2.4 +# * broken-d depends on broken-a (no version constraint) +# +# This allows us to test various upgrades, downgrades, and installs with broken dependencies. +# skip_broken should usually be successful in the upgrade/downgrade case, it will just do nothing. +# +# There is a nobest testcase or two thrown in, simply because this organization provides a very +# good test case for that behavior as well. For example, just installing "broken-a" with no version +# will try to install 2.0.0 which is broken. With nobest=true, it will fall back to 1.2.4. Similar +# for upgrading. +- block: + - name: Set up test yum repo + yum_repository: + name: skip-broken + description: ansible-test skip-broken test repo + baseurl: "{{ skip_broken_repo_baseurl }}" + gpgcheck: no + repo_gpgcheck: no + + - name: Install two packages + dnf: + name: + - broken-a-1.2.3 + - broken-b + + # This will fail. We have broken-a-1.2.3, and broken-b with a strong + # dependency on it. broken-c has a strong dependency on broken-a-1.2.4. + # Since installing that would break broken-b, we get a conflict. + - name: Try installing a third package, intentionally broken + dnf: + name: + - broken-c + ignore_errors: true + register: dnf_fail + + - assert: + that: + - dnf_fail is failed + - "'Depsolve Error' in dnf_fail.msg" + + # skip_broken should still install nothing because the conflict is + # still an issue. But it should skip over the broken packages and not + # fail. + - name: Try installing it with skip_broken + dnf: + name: + - broken-c + skip_broken: true + register: skip_broken_res + + - name: Assert that nothing got installed + assert: + that: + - skip_broken_res.msg == 'Nothing to do' + - skip_broken_res.rc == 0 + - skip_broken_res.results == [] + + - name: Remove all test packages + dnf: + name: + - broken-* + state: absent + + # broken-d depends on (unversioned) broken-a. + # broken-a-2.0.0 has a broken dependency that doesn't exist. + # skip_broken should cause us to skip our explicit broken-a-2.0.0 + # and bring in broken-a-1.2.4 as a dep of broken-d. + - name: Ensure proper failure with explicit broken version + dnf: + name: + - broken-a-2.0.0 + - broken-d + ignore_errors: true + register: dnf_fail + + - name: Assert that nothing got installed + assert: + that: + - dnf_fail is failed + - "'Depsolve Error' in dnf_fail.msg" + + - name: skip_broken with explicit version + dnf: + name: + - broken-a-2.0.0 + - broken-d + skip_broken: true + register: skip_broken_res + + - name: Assert that the right things got installed + assert: + that: + - skip_broken_res.rc == 0 + - skip_broken_res.results|length == 2 + - res.results|select("contains", "Installed: broken-a-1.2.4")|length > 0 + - res.results|select("contains", "Installed: broken-d-1.2.5")|length > 0 + + - name: Remove all test packages + dnf: + name: + - broken-* + state: absent + + # Walk the logic of _mark_package_install() here + # We need to use a full-ish NVR/wildcard. _is_newer_version_installed() + # will be false otherwise, no matter what. This might be a bug. + # Relatedly, the real "Case 1" in the code seemingly can't be reached: + # _is_newer_version_installed wants NVR, _is_installed wants name. + # Both can't be true at the same time given one pkg_spec. Thus, we start + # at "Case 2" + + # prereq + - name: Install broken-a-1.2.4 + dnf: + name: + - broken-a-1.2.4 + state: present + + # Case 2: newer version is installed, allow_downgrade is true, + # is_installed is false since we gave full NVR. + # "upgrade" to broken-a-1.2.3, allow_downgrade=true + - name: Do an "upgrade" to an older version of broken-a, allow_downgrade=true + dnf: + name: + - broken-a-1.2.3-1* + state: latest + allow_downgrade: true + check_mode: true + register: res + + - assert: + that: + - res is changed + - res.results|select("contains", "Installed: broken-a-1.2.3")|length > 0 + + # Still case 2, but with broken package to test skip_broken + # skip_broken: false + - name: Do an "upgrade" to an older known broken version of broken-a, allow_downgrade=true, skip_broken=false + dnf: + name: + - broken-a-1.2.3.4-1* + state: latest + allow_downgrade: true + check_mode: true + ignore_errors: true + register: res + + - assert: + that: + # 1.2.3.4 has non-existent dep. Fail without skip_broken. + - res is failed + - "'Depsolve Error' in res.msg" + + # skip_broken: true + - name: Do an "upgrade" to an older known broken version of broken-a, allow_downgrade=true, skip_broken=true + dnf: + name: + - broken-a-1.2.3.4-1* + state: latest + allow_downgrade: true + skip_broken: true + check_mode: true + register: res + + - assert: + that: + - res is not changed + - res.rc == 0 + - res.msg == "Nothing to do" + + # Case 3: newer version installed, allow_downgrade=true, but + # upgrade=false (i.e., state: present or installed) + - name: Install an older version of broken-a than currently installed + dnf: + name: + - broken-a-1.2.3-1* + state: present + allow_downgrade: true + check_mode: true + register: res + + - assert: + that: + - res is changed + - res.results|select("contains", "Installed: broken-a-1.2.3")|length > 0 + + # Case 3 still, with broken package and skip_broken tests like above. + - name: Install an older known broken version of broken-a, allow_downgrade=true, skip_broken=false + dnf: + name: + - broken-a-1.2.3.4-1* + state: present + allow_downgrade: true + check_mode: true + ignore_errors: true + register: res + + - assert: + that: + # 1.2.3.4 has non-existent dep. Fail without skip_broken. + - res is failed + - "'Depsolve Error' in res.msg" + + # skip_broken: true + - name: Install an older known broken version of broken-a, allow_downgrade=true, skip_broken=true + dnf: + name: + - broken-a-1.2.3.4-1* + state: present + allow_downgrade: true + skip_broken: true + check_mode: true + register: res + + - assert: + that: + - res is not changed + - res.rc == 0 + - res.msg == "Nothing to do" + + # Case 4: "upgrade" to broken-a-1.2.3, allow_downgrade=false + # is_newer_version_installed is true, allow_downgrade is false + - name: Do an "upgrade" to an older version of broken-a, allow_downgrade=false + dnf: + name: + - broken-a-1.2.3-1* + state: latest + allow_downgrade: false + check_mode: true + register: res + + - assert: + that: + - res is not changed + - res.rc == 0 + - res.msg == "Nothing to do" + + # skip_broken doesn't apply to case 5 or 6 (older version installed). + # base.upgrade doesn't allow a strict= kwarg. However, nobest works here. + + # Case 5: older version of package is installed, we specify name, no version + # otherwise we'd land in an earlier case. At this point, 1.2.4 is installed. + # broken-a-2.0.0 is available as an update but has a broken dependency. + - name: Update broken-a without nobest=true + dnf: + name: + - broken-a + state: latest + ignore_errors: true + register: dnf_fail + + - assert: + that: + - dnf_fail is failed + - "'Depsolve Error' in dnf_fail.msg" + + # With nobest: true, we will be "successful" but not actually perform + # any upgrade. That is, we are content not having the "best"/latest + # version. + - name: Update broken-a with nobest=true + dnf: + name: + - broken-a + state: latest + nobest: true + register: nobest + + - assert: + that: + - nobest.rc == 0 + - nobest.results == [] + + # Case 6: Current or older version already installed (no version specified + # in our pkg_spec) and we're requesting present, not latest. + # + # This isn't really relevant to skip_broken or nobest, but let's test it + # anyway since we're already walking the logic of the method. + - name: Install broken-a (even though it is already installed) + dnf: + name: + - broken-a + state: present + register: res + + - assert: + that: + - res is not changed + + # Case 7 is already tested quite extensively above in the earlier tests. + + always: + - name: Remove test yum repo + yum_repository: + name: skip-broken + state: absent + + - name: Remove all test packages installed + yum: + name: + - broken-* + state: absent diff --git a/test/integration/targets/dnf/tasks/test_sos_removal.yml b/test/integration/targets/dnf/tasks/test_sos_removal.yml new file mode 100644 index 0000000..40ceb62 --- /dev/null +++ b/test/integration/targets/dnf/tasks/test_sos_removal.yml @@ -0,0 +1,19 @@ +# These are safe to just check in check_mode, because in the module, the +# logic to match packages will happen anyway. check_mode will just prevent +# the transaction from actually running once the matches are found. +- name: Remove {{ item }} + dnf: + name: "{{ item }}" + state: absent + check_mode: true + register: sos_rm + +- debug: + var: sos_rm + +- assert: + that: + - sos_rm is successful + - sos_rm is changed + - "'Removed: sos-{{ sos_version }}-{{ sos_release }}' in sos_rm.results[0]" + - sos_rm.results|length == 1 |