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

Refresh metadata cache after failed package installation #3348

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
78 changes: 78 additions & 0 deletions tests/unit/test_package_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,84 @@ def test_install(
assert_output(expected_output, output.stdout, output.stderr)


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

for container, package_manager_class in CONTAINER_BASE_MATRIX:
if package_manager_class is tmt.package_managers.dnf.Yum:
yield container, \
package_manager_class, \
r"yum makecache", \
'Metadata'

elif package_manager_class is tmt.package_managers.dnf.Dnf:
yield container, \
package_manager_class, \
r"dnf makecache -y --refresh", \
'Metadata cache created'

elif package_manager_class is tmt.package_managers.dnf.Dnf5:
yield container, \
package_manager_class, \
r"dnf5 makecache -y --refresh", \
'Metadata cache created'

elif package_manager_class is tmt.package_managers.apt.Apt:
yield container, \
package_manager_class, \
r"export DEBIAN_FRONTEND=noninteractive; apt update", \
'packages'

elif package_manager_class is tmt.package_managers.rpm_ostree.RpmOstree:
yield container, \
package_manager_class, \
r"rpm-ostree upgrade --check", \
'Available'

elif package_manager_class is tmt.package_managers.apk.Apk:
yield container, \
package_manager_class, \
r"apk update", \
'OK:'

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_output'),
list(_parametrize_test_refresh_metadata()),
indirect=["container_per_test"],
ids=CONTAINER_MATRIX_IDS)
def test_refresh_metadata(
container_per_test: ContainerData,
guest_per_test: GuestContainer,
package_manager_class: PackageManagerClass,
expected_command: str,
expected_output: 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)

output = package_manager.refresh_metadata()

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

assert_output(expected_output, output.stdout, output.stderr)


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

Expand Down
3 changes: 3 additions & 0 deletions tmt/package_managers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,6 @@ def install_debuginfo(
*installables: Installable,
options: Optional[Options] = None) -> CommandOutput:
raise NotImplementedError

def refresh_metadata(self) -> CommandOutput:
raise NotImplementedError
6 changes: 6 additions & 0 deletions tmt/package_managers/apk.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ def check_presence(self, *installables: Installable) -> dict[Installable, bool]:

return results

def refresh_metadata(self) -> CommandOutput:
script = ShellScript(
f'{self.command.to_script()} update')

return self.guest.execute(script)

def install(
self,
*installables: Installable,
Expand Down
8 changes: 8 additions & 0 deletions tmt/package_managers/apt.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ def _extra_options(self, options: Options) -> Command:

return extra_options

def refresh_metadata(self) -> CommandOutput:
script = ShellScript(
f'{self.command.to_script()} update')

return self.guest.execute(script, env=Environment({
'DEBIAN_FRONTEND': EnvVarValue('noninteractive')
}))

def install(
self,
*installables: Installable,
Expand Down
13 changes: 13 additions & 0 deletions tmt/package_managers/dnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ def _construct_install_debuginfo_script(
f'{self.options.to_script()} {extra_options} '
f'{" ".join(escape_installables(*installables))}')

def refresh_metadata(self) -> CommandOutput:
script = ShellScript(
f'{self.command.to_script()} makecache '
f'{self.options.to_script()} --refresh')

return self.guest.execute(script)

def install(
self,
*installables: Installable,
Expand Down Expand Up @@ -277,3 +284,9 @@ def reinstall(
*installables))

return self.guest.execute(script)

def refresh_metadata(self) -> CommandOutput:
script = ShellScript(
f'{self.command.to_script()} makecache')

return self.guest.execute(script)
6 changes: 6 additions & 0 deletions tmt/package_managers/rpm_ostree.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ def _extra_options(

return extra_options

def refresh_metadata(self) -> CommandOutput:
script = ShellScript(
f'{self.command.to_script()} upgrade --check')

return self.guest.execute(script)

def install(
self,
*installables: Installable,
Expand Down
18 changes: 18 additions & 0 deletions tmt/steps/prepare/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ def install_debuginfo(self) -> None:

def install(self) -> None:
""" Perform the actual installation """
try:
self._install()

except Exception as exc1:
# We do not have any special handling for exceptions raised by the following code.
# Wrapping them with try/except gives us a chance to attach the original exception
# to whatever the code may raise, and therefore preserve the information attached
# to the original exception.
try:
# Refresh cache in case of recent but not updated change do repodata
self.guest.package_manager.refresh_metadata()
self._install()

except Exception as exc2:
raise exc2 from exc1

def _install(self) -> None:
""" Helper method to perform the actual installation steps """
if self.local_packages:
self.prepare_install_local()
self.install_local()
Expand Down
Loading