# 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* - broken-a-1.2.3-1.el7.x86_64 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 best: true 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. # ###################################################################### - block: - name: Test available package is installed while unavailable one is skipped due to skip_broken=true dnf: name: - dinginessentail - not-in-repo skip_broken: true register: res - assert: that: - res is changed # There is a change in dnf5 that splits skip_broken into two options: # skip_broken and skip_unavailable. # TODO: for this test to pass on dnf5 the skip_unavailable option would have to be # added to the dnf5 module and used here instead of skip_broken. when: not dnf5|default(false) always: - name: Remove test yum repo yum_repository: name: skip-broken state: absent - name: Remove all test packages installed dnf: name: - broken-* - dinginessentail state: absent