Skip to content

Commit fa4b4b9

Browse files
matejmatuskapirat89
authored andcommitted
Copy dnf.conf to target userspace and allow a custom one
This change allows working around the fact that source and target `dnf.conf` files might be incompatible. For example some of the proxy configuration between RHEL7 and RHEL8. Target system compatible configuration can be specified in /etc/leapp/files/dnf.conf. If this file is present it is copied into the target userspace and also applied to the target system. If it doesn't exist, the `/etc/dnf/dnf.conf` from the source system will be copied instead. Errors that could be caused by incompatible/incorrect proxy configuration now contain a hint with a remediation with the steps above mentioned. * [email protected]: Updated text in the error msg. Jira: OAMG-6544
1 parent 5a3bded commit fa4b4b9

File tree

8 files changed

+163
-5
lines changed

8 files changed

+163
-5
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from leapp.actors import Actor
2+
from leapp.libraries.actor import applycustomdnfconf
3+
from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
4+
5+
6+
class ApplyCustomDNFConf(Actor):
7+
"""
8+
Move /etc/leapp/files/dnf.conf to /etc/dnf/dnf.conf if it exists
9+
10+
An actor in FactsPhase copies this file to the target userspace if present.
11+
In such case we also want to use the file on the target system.
12+
"""
13+
name = "apply_custom_dnf_conf"
14+
consumes = ()
15+
produces = ()
16+
tags = (ApplicationsPhaseTag, IPUWorkflowTag)
17+
18+
def process(self):
19+
applycustomdnfconf.process()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
3+
from leapp.libraries.stdlib import api, CalledProcessError, run
4+
5+
CUSTOM_DNF_CONF_PATH = "/etc/leapp/files/dnf.conf"
6+
7+
8+
def process():
9+
if os.path.exists(CUSTOM_DNF_CONF_PATH):
10+
try:
11+
run(["mv", CUSTOM_DNF_CONF_PATH, "/etc/dnf/dnf.conf"])
12+
except (CalledProcessError, OSError) as e:
13+
api.current_logger().debug(
14+
"Failed to move /etc/leapp/files/dnf.conf to /etc/dnf/dnf.conf: {}".format(e)
15+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
3+
import pytest
4+
5+
from leapp.libraries.actor import applycustomdnfconf
6+
7+
8+
@pytest.mark.parametrize(
9+
"exists,should_move",
10+
[(False, False), (True, True)],
11+
)
12+
def test_copy_correct_dnf_conf(monkeypatch, exists, should_move):
13+
monkeypatch.setattr(os.path, "exists", lambda _: exists)
14+
15+
run_called = [False]
16+
17+
def mocked_run(_):
18+
run_called[0] = True
19+
20+
monkeypatch.setattr(applycustomdnfconf, 'run', mocked_run)
21+
22+
applycustomdnfconf.process()
23+
assert run_called[0] == should_move
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from leapp.actors import Actor
2+
from leapp.libraries.actor import copydnfconfintotargetuserspace
3+
from leapp.models import TargetUserSpacePreupgradeTasks
4+
from leapp.tags import FactsPhaseTag, IPUWorkflowTag
5+
6+
7+
class CopyDNFConfIntoTargetUserspace(Actor):
8+
"""
9+
Copy dnf.conf into target userspace
10+
11+
Copies /etc/leapp/files/dnf.conf to target userspace. If it isn't available
12+
/etc/dnf/dnf.conf is copied instead. This allows specifying a different
13+
config for the target userspace, which might be required if the source
14+
system configuration file isn't compatible with the target one. One such
15+
example is incompatible proxy configuration between RHEL7 and RHEL8 DNF
16+
versions.
17+
"""
18+
name = "copy_dnf_conf_into_target_userspace"
19+
consumes = ()
20+
produces = (TargetUserSpacePreupgradeTasks,)
21+
tags = (FactsPhaseTag, IPUWorkflowTag)
22+
23+
def process(self):
24+
copydnfconfintotargetuserspace.process()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
3+
from leapp.libraries.stdlib import api
4+
from leapp.models import CopyFile, TargetUserSpacePreupgradeTasks
5+
6+
7+
def process():
8+
src = "/etc/dnf/dnf.conf"
9+
if os.path.exists("/etc/leapp/files/dnf.conf"):
10+
src = "/etc/leapp/files/dnf.conf"
11+
12+
api.current_logger().debug(
13+
"Copying dnf.conf at {} to the target userspace".format(src)
14+
)
15+
api.produce(
16+
TargetUserSpacePreupgradeTasks(
17+
copy_files=[CopyFile(src=src, dst="/etc/dnf/dnf.conf")]
18+
)
19+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import os
2+
3+
import pytest
4+
5+
from leapp.libraries.actor import copydnfconfintotargetuserspace
6+
from leapp.libraries.common.testutils import logger_mocked, produce_mocked
7+
8+
9+
@pytest.mark.parametrize(
10+
"userspace_conf_exists,expected",
11+
[(False, "/etc/dnf/dnf.conf"), (True, "/etc/leapp/files/dnf.conf")],
12+
)
13+
def test_copy_correct_dnf_conf(monkeypatch, userspace_conf_exists, expected):
14+
monkeypatch.setattr(os.path, "exists", lambda _: userspace_conf_exists)
15+
16+
mocked_produce = produce_mocked()
17+
monkeypatch.setattr(copydnfconfintotargetuserspace.api, 'produce', mocked_produce)
18+
monkeypatch.setattr(copydnfconfintotargetuserspace.api, 'current_logger', logger_mocked())
19+
20+
copydnfconfintotargetuserspace.process()
21+
22+
assert mocked_produce.called == 1
23+
assert len(mocked_produce.model_instances) == 1
24+
assert len(mocked_produce.model_instances[0].copy_files) == 1
25+
assert mocked_produce.model_instances[0].copy_files[0].src == expected
26+
assert mocked_produce.model_instances[0].copy_files[0].dst == "/etc/dnf/dnf.conf"

repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,25 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages):
269269
# failed since leapp does not support updates behind proxy yet.
270270
for manager_info in api.consume(PkgManagerInfo):
271271
if manager_info.configured_proxies:
272-
details['details'] = ("DNF failed to install userspace packages, likely due to the proxy "
273-
"configuration detected in the YUM/DNF configuration file.")
272+
details['details'] = (
273+
"DNF failed to install userspace packages, likely due to the proxy "
274+
"configuration detected in the YUM/DNF configuration file. "
275+
"Make sure the proxy is properly configured in /etc/dnf/dnf.conf. "
276+
"It's also possible the proxy settings in the DNF configuration file are "
277+
"incompatible with the target system. A compatible configuration can be "
278+
"placed in /etc/leapp/files/dnf.conf which, if present, will be used during "
279+
"the upgrade instead of /etc/dnf/dnf.conf. "
280+
"In such case the configuration will also be applied to the target system."
281+
)
274282

275283
# Similarly if a proxy was set specifically for one of the repositories.
276284
for repo_facts in api.consume(RepositoriesFacts):
277285
for repo_file in repo_facts.repositories:
278286
if any(repo_data.proxy and repo_data.enabled for repo_data in repo_file.data):
279-
details['details'] = ("DNF failed to install userspace packages, likely due to the proxy "
280-
"configuration detected in a repository configuration file.")
287+
details['details'] = (
288+
"DNF failed to install userspace packages, likely due to the proxy "
289+
"configuration detected in a repository configuration file."
290+
)
281291

282292
raise StopActorExecutionError(message=message, details=details)
283293

repos/system_upgrade/common/libraries/dnfplugin.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,30 @@ def _handle_transaction_err_msg(stage, xfs_info, err, is_container=False):
178178
return # not needed actually as the above function raises error, but for visibility
179179
NO_SPACE_STR = 'more space needed on the'
180180
message = 'DNF execution failed with non zero exit code.'
181-
details = {'STDOUT': err.stdout, 'STDERR': err.stderr}
182181
if NO_SPACE_STR not in err.stderr:
182+
# if there was a problem reaching repos and proxy is configured in DNF/YUM configs, the
183+
# proxy is likely the problem.
184+
# NOTE(mmatuska): We can't consistently detect there was a problem reaching some repos,
185+
# because it isn't clear what are all the possible DNF error messages we can encounter,
186+
# such as: "Failed to synchronize cache for repo ..." or "Errors during downloading
187+
# metadata for # repository" or "No more mirrors to try - All mirrors were already tried
188+
# without success"
189+
# NOTE(mmatuska): We could check PkgManagerInfo to detect if proxy is indeed configured,
190+
# however it would be pretty ugly to pass it all the way down here
191+
proxy_hint = (
192+
"If there was a problem reaching remote content (see stderr output) and proxy is "
193+
"configured in the YUM/DNF configuration file, the proxy configuration is likely "
194+
"causing this error. "
195+
"Make sure the proxy is properly configured in /etc/dnf/dnf.conf. "
196+
"It's also possible the proxy settings in the DNF configuration file are "
197+
"incompatible with the target system. A compatible configuration can be "
198+
"placed in /etc/leapp/files/dnf.conf which, if present, it will be used during "
199+
"some parts of the upgrade instead of original /etc/dnf/dnf.conf. "
200+
"In such case the configuration will also be applied to the target system. "
201+
"Note that /etc/dnf/dnf.conf needs to be still configured correctly "
202+
"for your current system to pass the early phases of the upgrade process."
203+
)
204+
details = {'STDOUT': err.stdout, 'STDERR': err.stderr, 'hint': proxy_hint}
183205
raise StopActorExecutionError(message=message, details=details)
184206

185207
# Disk Requirements:

0 commit comments

Comments
 (0)