Skip to content

Commit 104050f

Browse files
committed
Prepare metadata cache before package installation
1 parent cf2d833 commit 104050f

File tree

7 files changed

+145
-0
lines changed

7 files changed

+145
-0
lines changed

tests/unit/test_package_managers.py

+81
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,87 @@ def test_install(
440440
assert_output(expected_output, output.stdout, output.stderr)
441441

442442

443+
def _parametrize_test_refresh_metadata() -> \
444+
Iterator[tuple[
445+
Container,
446+
PackageManagerClass,
447+
str,
448+
Optional[str]]]:
449+
450+
for container, package_manager_class in CONTAINER_BASE_MATRIX:
451+
if package_manager_class is tmt.package_managers.dnf.Yum:
452+
yield container, \
453+
package_manager_class, \
454+
r"yum makecache", \
455+
'Metadata'
456+
457+
elif package_manager_class is tmt.package_managers.dnf.Dnf:
458+
yield container, \
459+
package_manager_class, \
460+
r"dnf makecache -y --refresh", \
461+
'Metadata cache created'
462+
463+
elif package_manager_class is tmt.package_managers.dnf.Dnf5:
464+
yield container, \
465+
package_manager_class, \
466+
r"dnf5 makecache -y --refresh", \
467+
'Metadata cache created'
468+
469+
elif package_manager_class is tmt.package_managers.apt.Apt:
470+
yield container, \
471+
package_manager_class, \
472+
r"export DEBIAN_FRONTEND=noninteractive; apt update", \
473+
'packages'
474+
475+
elif package_manager_class is tmt.package_managers.rpm_ostree.RpmOstree:
476+
yield pytest.param(
477+
container,
478+
package_manager_class,
479+
r"rpm-ostree refresh-md --force",
480+
'Available',
481+
marks=pytest.mark.skip(reason="refresh-md does not work with how tmt runs ostree container") # noqa: E501
482+
)
483+
484+
elif package_manager_class is tmt.package_managers.apk.Apk:
485+
yield container, \
486+
package_manager_class, \
487+
r"apk update", \
488+
'OK:'
489+
490+
else:
491+
pytest.fail(f"Unhandled package manager class '{package_manager_class}'.")
492+
493+
494+
@pytest.mark.containers
495+
@pytest.mark.parametrize(('container_per_test',
496+
'package_manager_class',
497+
'expected_command',
498+
'expected_output'),
499+
list(_parametrize_test_refresh_metadata()),
500+
indirect=["container_per_test"],
501+
ids=CONTAINER_MATRIX_IDS)
502+
def test_refresh_metadata(
503+
container_per_test: ContainerData,
504+
guest_per_test: GuestContainer,
505+
package_manager_class: PackageManagerClass,
506+
expected_command: str,
507+
expected_output: Optional[str],
508+
root_logger: tmt.log.Logger,
509+
caplog: _pytest.logging.LogCaptureFixture) -> None:
510+
package_manager = create_package_manager(
511+
container_per_test,
512+
guest_per_test,
513+
package_manager_class,
514+
root_logger)
515+
516+
output = package_manager.refresh_metadata()
517+
518+
assert_log(caplog, message=MATCH(
519+
rf"Run command: podman exec .+? /bin/bash -c '{expected_command}'"))
520+
521+
assert_output(expected_output, output.stdout, output.stderr)
522+
523+
443524
def _parametrize_test_install_nonexistent() -> \
444525
Iterator[tuple[Container, PackageManagerClass, str, Optional[str]]]:
445526

tmt/package_managers/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,6 @@ def install_debuginfo(
186186
*installables: Installable,
187187
options: Optional[Options] = None) -> CommandOutput:
188188
raise NotImplementedError
189+
190+
def refresh_metadata(self) -> CommandOutput:
191+
raise NotImplementedError

tmt/package_managers/apk.py

+6
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ def check_presence(self, *installables: Installable) -> dict[Installable, bool]:
122122

123123
return results
124124

125+
def refresh_metadata(self) -> CommandOutput:
126+
script = ShellScript(
127+
f'{self.command.to_script()} update')
128+
129+
return self.guest.execute(script)
130+
125131
def install(
126132
self,
127133
*installables: Installable,

tmt/package_managers/apt.py

+8
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ def _extra_options(self, options: Options) -> Command:
139139

140140
return extra_options
141141

142+
def refresh_metadata(self) -> CommandOutput:
143+
script = ShellScript(
144+
f'{self.command.to_script()} update')
145+
146+
return self.guest.execute(script, env=Environment({
147+
'DEBIAN_FRONTEND': EnvVarValue('noninteractive')
148+
}))
149+
142150
def install(
143151
self,
144152
*installables: Installable,

tmt/package_managers/dnf.py

+13
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ def _construct_install_debuginfo_script(
155155
f'{self.options.to_script()} {extra_options} '
156156
f'{" ".join(escape_installables(*installables))}')
157157

158+
def refresh_metadata(self) -> CommandOutput:
159+
script = ShellScript(
160+
f'{self.command.to_script()} makecache '
161+
f'{self.options.to_script()} --refresh')
162+
163+
return self.guest.execute(script)
164+
158165
def install(
159166
self,
160167
*installables: Installable,
@@ -277,3 +284,9 @@ def reinstall(
277284
*installables))
278285

279286
return self.guest.execute(script)
287+
288+
def refresh_metadata(self) -> CommandOutput:
289+
script = ShellScript(
290+
f'{self.command.to_script()} makecache')
291+
292+
return self.guest.execute(script)

tmt/package_managers/rpm_ostree.py

+16
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ def _extra_options(
100100

101101
return extra_options
102102

103+
def refresh_metadata(self) -> CommandOutput:
104+
self.guest.warn("Metadata refresh is not supported with rpm-ostree.")
105+
106+
return CommandOutput(stdout=None, stderr=None)
107+
108+
# The following should work, but it hits some ostree issue:
109+
#
110+
# System has not been booted with systemd as init system (PID 1). Can't operate.
111+
# Failed to connect to bus: Host is down
112+
# System has not been booted with systemd as init system (PID 1). Can't operate.
113+
# Failed to connect to bus: Host is down
114+
# error: Loading sysroot: exit status: 1
115+
#
116+
# script = ShellScript(f'{self.command.to_script()} refresh-md --force')
117+
# return self.guest.execute(script)
118+
103119
def install(
104120
self,
105121
*installables: Installable,

tmt/steps/prepare/install.py

+18
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ def install_debuginfo(self) -> None:
165165

166166
def install(self) -> None:
167167
""" Perform the actual installation """
168+
try:
169+
self._install()
170+
171+
except Exception as exc1:
172+
# We do not have any special handling for exceptions raised by the following code.
173+
# Wrapping them with try/except gives us a chance to attach the original exception
174+
# to whatever the code may raise, and therefore preserve the information attached
175+
# to the original exception.
176+
try:
177+
# Refresh cache in case of recent but not updated change do repodata
178+
self.guest.package_manager.refresh_metadata()
179+
self._install()
180+
181+
except Exception as exc2:
182+
raise exc2 from exc1
183+
184+
def _install(self) -> None:
185+
""" Helper method to perform the actual installation steps """
168186
if self.local_packages:
169187
self.prepare_install_local()
170188
self.install_local()

0 commit comments

Comments
 (0)