Skip to content

Commit

Permalink
Fix installation of packages from files (#2914)
Browse files Browse the repository at this point in the history
Fixes #2911
  • Loading branch information
happz authored May 7, 2024
1 parent 2879190 commit 142f3d1
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 5 deletions.
6 changes: 6 additions & 0 deletions tests/prepare/install/data/downloaded.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
summary: Install downloaded packages
prepare:
- how: install
package:
- tree.rpm
- diffutils.rpm
43 changes: 43 additions & 0 deletions tests/prepare/install/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ function is_ubi () {
[[ "$1" =~ ^.*ubi.* ]] && return 0 || return 1
}

function fetch_downloaded_packages () {
if [ ! -e $package_cache/tree.rpm ]; then
# For some reason, this command will get stuck in rlRun...
container_id="$(podman run -d $1 sleep 3600)"

rlRun "podman exec $container_id bash -c \"set -x; \
dnf install -y 'dnf-command(download)' \
&& dnf download --destdir /tmp tree diffutils \
&& mv /tmp/tree*.rpm /tmp/tree.rpm \
&& mv /tmp/diffutils*.rpm /tmp/diffutils.rpm\""
rlRun "podman cp $container_id:/tmp/tree.rpm $package_cache/"
rlRun "podman cp $container_id:/tmp/diffutils.rpm $package_cache/"
rlRun "podman kill $container_id"
rlRun "podman rm $container_id"
fi

rlRun "cp $package_cache/tree.rpm ./"
rlRun "cp $package_cache/diffutils.rpm ./"
}

rlJournalStart
rlPhaseStartSetup
Expand All @@ -107,6 +126,7 @@ rlJournalStart
rlRun "IMAGES="
fi

rlRun "package_cache=\$(mktemp -d)" 0 "Create cache directory for downloaded packages"
rlRun "run=\$(mktemp -d)" 0 "Create run directory"
rlRun "pushd data"

Expand Down Expand Up @@ -205,6 +225,28 @@ rlJournalStart
rlPhaseEnd
fi

if rlIsFedora 39 && is_fedora_39 "$image"; then
rlPhaseStartTest "$phase_prefix Install downloaded packages (plan)"
fetch_downloaded_packages "$image"

rlRun -s "$tmt plan --name /downloaded"

rlAssertGrep "package manager: $package_manager" $rlRun_LOG

rlAssertGrep "summary: 2 preparations applied" $rlRun_LOG
rlPhaseEnd

rlPhaseStartTest "$phase_prefix Install downloaded packages (CLI)"
fetch_downloaded_packages "$image"

rlRun -s "$tmt prepare --insert --how install --package tree*.rpm --package diffutils*.rpm plan --name /empty"

rlAssertGrep "package manager: $package_manager" $rlRun_LOG

rlAssertGrep "summary: 2 preparations applied" $rlRun_LOG
rlPhaseEnd
fi

rlPhaseStartTest "$phase_prefix Install existing and invalid packages (plan)"
rlRun -s "$tmt plan --name /missing" 2

Expand Down Expand Up @@ -306,6 +348,7 @@ rlJournalStart

rlPhaseStartCleanup
rlRun "popd"
rlRun "rm -r $package_cache" 0 "Remove package cache directory"
rlRun "rm -r $run" 0 "Remove run directory"
rlPhaseEnd
rlJournalEnd
135 changes: 135 additions & 0 deletions tests/unit/test_package_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Package,
PackageManager,
PackageManagerClass,
PackagePath,
)
from tmt.steps.provision.podman import GuestContainer, PodmanGuestData
from tmt.utils import ShellScript
Expand Down Expand Up @@ -1351,3 +1352,137 @@ def test_install_multiple(
if expected_stderr:
assert output.stderr is not None
assert expected_stderr in output.stderr


def _parametrize_test_install_downloaded() -> \
Iterator[tuple[Container, PackageManagerClass, str, Optional[str], Optional[str]]]:

for container, package_manager_class in CONTAINER_BASE_MATRIX:
if package_manager_class is tmt.package_managers.dnf.Yum:
if 'centos:7' in container.url:
yield pytest.param(
container,
package_manager_class,
r"yum install -y --skip-broken /tmp/tree.rpm /tmp/diffutils.rpm \|\| /bin/true", # noqa: E501
'Complete!',
None,
marks=pytest.mark.skip(reason="CentOS 7 does not support 'download' command")
)

else:
yield container, \
package_manager_class, \
r"yum install -y --skip-broken /tmp/tree.rpm /tmp/diffutils.rpm \|\| /bin/true", \
'Complete!', \
None # noqa: E501

elif package_manager_class is tmt.package_managers.dnf.Dnf:
yield container, \
package_manager_class, \
r"dnf install -y /tmp/tree.rpm /tmp/diffutils.rpm", \
'Complete!', \
None

elif package_manager_class is tmt.package_managers.dnf.Dnf5:
yield container, \
package_manager_class, \
r"dnf5 install -y /tmp/tree.rpm /tmp/diffutils.rpm", \
None, \
None

elif package_manager_class is tmt.package_managers.rpm_ostree.RpmOstree:
yield container, \
package_manager_class, \
r"rpm-ostree install --apply-live --idempotent --allow-inactive /tmp/tree.rpm /tmp/diffutils.rpm", \
'Installing: tree', \
None # noqa: E501

elif package_manager_class is tmt.package_managers.apt.Apt:
yield pytest.param(
container,
package_manager_class,
r"export DEBIAN_FRONTEND=noninteractive; dpkg-query --show tree diffutils \|\| apt install -y tree diffutils", # noqa: E501
'Setting up tree',
None,
marks=pytest.mark.skip(reason="not supported yet")
)

elif package_manager_class is tmt.package_managers.apk.Apk:
yield pytest.param(
container,
package_manager_class,
r"apk info -e tree diffutils \|\| apk add tree diffutils",
'Installing tree',
None,
marks=pytest.mark.skip(reason="not supported yet")
)

else:
pytest.fail(f"Unhandled package manager class '{package_manager_class}'.")


@pytest.mark.containers()
@pytest.mark.parametrize(('container_per_test',
'package_manager_class',
'expected_command',
'expected_stdout',
'expected_stderr'),
list(_parametrize_test_install_downloaded()),
indirect=["container_per_test"],
ids=CONTAINER_MATRIX_IDS)
def test_install_downloaded(
container_per_test: ContainerData,
guest_per_test: GuestContainer,
package_manager_class: PackageManagerClass,
expected_command: str,
expected_stdout: Optional[str],
expected_stderr: Optional[str],
root_logger: tmt.log.Logger,
caplog: _pytest.logging.LogCaptureFixture) -> None:
package_manager = create_package_manager(
container_per_test,
guest_per_test,
package_manager_class,
root_logger)

# TODO: move to a fixture
guest_per_test.execute(ShellScript(
"""
(yum download --destdir /tmp tree diffutils \
|| (dnf install -y 'dnf-command(download)' && dnf download --destdir /tmp tree diffutils) \
|| (dnf5 install -y 'dnf-command(download)' && dnf5 download --destdir /tmp tree diffutils)) \
&& mv /tmp/tree*.rpm /tmp/tree.rpm && mv /tmp/diffutils*.rpm /tmp/diffutils.rpm
""")) # noqa: E501

# TODO: yum and downloaded packages results in post-install `rpm -q`
# check to make sure packages were indeed installed - but that
# breaks because that test uses original package paths, not package
# names, and fails. Disable this check for now, but it's a sloppy
# "solution".
if package_manager_class is tmt.package_managers.dnf.Yum:
output = package_manager.install(
PackagePath('/tmp/tree.rpm'),
PackagePath('/tmp/diffutils.rpm'),
options=Options(
check_first=False,
skip_missing=True
))

else:
output = package_manager.install(
PackagePath('/tmp/tree.rpm'),
PackagePath('/tmp/diffutils.rpm'),
options=Options(
check_first=False
))

assert_log(caplog, message=MATCH(
rf"Run command: podman exec .+? /bin/bash -c '{expected_command}'"))

if expected_stdout:
assert output.stdout is not None
assert expected_stdout in output.stdout

if expected_stderr:
assert output.stderr is not None
assert expected_stderr in output.stderr
18 changes: 13 additions & 5 deletions tmt/steps/prepare/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,17 @@ def install_local(self) -> None:
*filelist,
options=Options(
excluded_packages=self.exclude,
skip_missing=self.skip_missing
skip_missing=self.skip_missing,
check_first=False
)
)

self.guest.package_manager.reinstall(
*filelist,
options=Options(
excluded_packages=self.exclude,
skip_missing=self.skip_missing
skip_missing=self.skip_missing,
check_first=False
)
)

Expand Down Expand Up @@ -385,7 +387,11 @@ def install_local(self) -> None:
for package in self.local_packages:
try:
self.guest.package_manager.install(
PackagePath(self.package_directory / package.name))
PackagePath(self.package_directory / package.name),
options=Options(
check_first=False
)
)
local_packages_installed.append(package)
except tmt.utils.RunError as error:
self.warn(f"Local package '{package}' not installed: {error.stderr}")
Expand Down Expand Up @@ -426,7 +432,8 @@ def install_local(self) -> None:
*self.list_installables('local packages', *filelist),
options=Options(
excluded_packages=self.exclude,
skip_missing=self.skip_missing
skip_missing=self.skip_missing,
check_first=False
)
)

Expand Down Expand Up @@ -488,7 +495,8 @@ def install_local(self) -> None:
options=Options(
excluded_packages=self.exclude,
skip_missing=self.skip_missing,
allow_untrusted=True
allow_untrusted=True,
check_first=False
)
)

Expand Down

0 comments on commit 142f3d1

Please sign in to comment.