Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace test_zypper_dup_works with test_no_downgrade_on_install #329

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 70 additions & 25 deletions tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
"""
import datetime
import fnmatch
import json
import xml.etree.ElementTree as ET
dirkmueller marked this conversation as resolved.
Show resolved Hide resolved

import packaging.version
import pytest
from _pytest.config import Config
from pytest_container import Container
Expand Down Expand Up @@ -214,36 +216,83 @@ def test_glibc_present(auto_container):
assert auto_container.connection.exists(binary)


@pytest.mark.skipif(
OS_VERSION not in ALLOWED_BCI_REPO_OS_VERSIONS,
reason="LTSS containers are known to be non-functional with BCI_repo ",
)
@pytest.mark.skipif(
OS_VERSION == "basalt",
reason="Basalt repos are known to be out of sync with IBS state",
)
@pytest.mark.parametrize("container", CONTAINERS_WITH_ZYPPER, indirect=True)
def test_no_downgrade_on_install(container: ContainerData) -> None:
"""Check that we can install any additional pacakage in the container.

Check that installing any additional package would not cause a downgrade
of any package already installed in the container as that would throw
a question to the user and break the builds.
"""

conn = container.connection

conn.run_expect([0], "timeout 2m zypper ref")
system_solv = json.loads(
conn.check_output("dumpsolv -j /var/cache/zypp/solv/@System/solv")
)
bci_solv = json.loads(
conn.check_output(
f"dumpsolv -j /var/cache/zypp/solv/{BCI_REPO_NAME}/solv"
)
)
installed_pkgs = {
solvable["solvable:name"]: solvable["solvable:evr"]
for solvable in system_solv["repositories"][0]["solvables"]
}
bci_pkgs = {}
for solvable in bci_solv["repositories"][0]["solvables"]:
bci_pkgs[solvable["solvable:name"]] = solvable["solvable:evr"]
if solvable["solvable:name"] in installed_pkgs:
continue
for req in solvable.get("solvable:requires", ()):
# Skip boolean dependencies or unversioned ones
if "(" in req or " = " not in req:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will also skip virtual requires like pkgconfig(libfoo) = 1.2.3.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcermak yeah, I didn't want to implement provides/requires matching logic. so far it was finding the essential issues without that (as typically people do Requires: %name = %version-%release somewhere. we can always improve that later if it is necessary to find more issues (but already now it finds too many :/ )

continue
name, _, version = req.partition(" = ")
if name in installed_pkgs:
installed_version, _, installed_release = installed_pkgs[
name
].partition("-")
version, _, release = version.partition("-")
if installed_version == version and release:
assert packaging.version.parse(
installed_release
) <= packaging.version.parse(release), (
f"Installed {name} = {installed_release} is newer than "
f"what {solvable['solvable:name']} requires (= {release})"
)


@pytest.mark.skipif(
OS_VERSION not in ALLOWED_BCI_REPO_OS_VERSIONS,
reason="LTSS containers are known to be non-functional with BCI_repo ",
)
@pytest.mark.parametrize(
"container_per_test", CONTAINERS_WITH_ZYPPER, indirect=True
)
def test_zypper_dup_works(container_per_test: ContainerData) -> None:
"""Check that there are no packages installed that we wouldn't find in SLE
BCI repo by running :command:`zypper -n dup` and checking that there are no
conflicts or arch changes and we can update to the state in SLE_BCI repos.
Then validate that SLE_BCI provides all the packages that are afterwards
in the container as well except for the known intentional breakages
(sles-release, skelcd-EULA-bci).

As of 2023-05 the container and the SLE_BCI repositories are released independently
so we frequently get downgrades in this test. allow --allow-downgrade therefore
but still test that there wouldn't be conflicts with what is available in SLE_BCI.
def test_no_orphaned_packages(container_per_test: ContainerData) -> None:
"""Check that containers do not contain any package that isn't also
available via repositories.
"""

container_per_test.connection.run_expect(
[0],
container_per_test.connection.check_output(
f"timeout 5m zypper -n dup --from {BCI_REPO_NAME} -l "
"--no-allow-vendor-change --allow-downgrade --no-allow-arch-change",
"--no-allow-vendor-change --allow-downgrade --no-allow-arch-change"
)

searchresult = ET.fromstring(
container_per_test.connection.run_expect(
[0], "zypper -x -n search -t package -v -i '*'"
).stdout
container_per_test.connection.check_output(
"zypper -x -n search -t package -v -i '*'"
)
)

orphaned_packages = {
Expand All @@ -252,7 +301,6 @@ def test_zypper_dup_works(container_per_test: ContainerData) -> None:
'search-result/solvable-list/solvable[@repository="(System Packages)"]'
)
}

# kubic-locale-archive should be replaced by glibc-locale-base in the containers
# but that is a few bytes larger so we accept it as an exception
known_orphaned_packages = {
Expand All @@ -262,21 +310,18 @@ def test_zypper_dup_works(container_per_test: ContainerData) -> None:
"sles-release",
"ALP-dummy-release",
}

assert not orphaned_packages.difference(known_orphaned_packages)


@pytest.mark.parametrize(
"container_per_test", CONTAINERS_WITH_ZYPPER, indirect=True
)
def test_zypper_verify_passes(container_per_test: ContainerData) -> None:
@pytest.mark.parametrize("container", CONTAINERS_WITH_ZYPPER, indirect=True)
def test_zypper_verify_passes(container: ContainerData) -> None:
"""Check that there are no packages missing according to zypper verify so that
users of the container would not get excessive dependencies installed.
"""
assert (
"Dependencies of all installed packages are satisfied."
in container_per_test.connection.check_output(
"timeout 5m env LC_ALL=C zypper -n verify -D"
in container.connection.check_output(
"timeout 5m env LC_ALL=C zypper --no-refresh -n verify -D"
)
)

Expand Down