Skip to content

Commit 6d4f9ea

Browse files
Monstrofilprilr
andauthored
AlmaLinux vendors.d functionaloty rebased on top of v0.19 (AlmaLinux#114)
* Reimplement the vendors mechanism on top of 0.19.0 * Move the RepoMapData class to a common library * Add the missing function to the new library * Add multiple repomap support to PES Events Scanner * Ensure the repositories data is available to the vendors-checking actors * Add target directory to the PES fetch function * Allow packageinfo to be explicitly empty list in pes files Raise errors only when packageinfo is missing, but allow empty lists which mean that no actions required. * Implement package reinstallation * Add the missing reinstallation ID * TODO: Reinstallation * Bring back the package reinstallation * Fix type errors in in code * Add additional trusted gpg certificates directory * Added missing signatures --------- Co-authored-by: Roman Prilipskii <[email protected]> Co-authored-by: Oleksandr Shyshatskyi <[email protected]>
1 parent 2552782 commit 6d4f9ea

File tree

31 files changed

+867
-201
lines changed

31 files changed

+867
-201
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ ENV/
115115

116116
# visual studio code configuration
117117
.vscode
118+
*.code-workspace
118119

119120
# pycharm
120121
.idea

etc/leapp/transaction/to_reinstall

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### List of packages (each on new line) to be reinstalled to the upgrade transaction
2+
### Useful for packages that have identical version strings but contain binary changes between major OS versions
3+
### Packages that aren't installed will be skipped
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from leapp.actors import Actor
2+
from leapp.libraries.stdlib import api
3+
from leapp.models import (
4+
RepositoriesFacts,
5+
VendorSourceRepos,
6+
ActiveVendorList,
7+
)
8+
from leapp.tags import FactsPhaseTag, IPUWorkflowTag
9+
10+
11+
class CheckEnabledVendorRepos(Actor):
12+
"""
13+
Create a list of vendors whose repositories are present on the system and enabled.
14+
Only those vendors' configurations (new repositories, PES actions, etc.)
15+
will be included in the upgrade process.
16+
"""
17+
18+
name = "check_enabled_vendor_repos"
19+
consumes = (RepositoriesFacts, VendorSourceRepos)
20+
produces = (ActiveVendorList)
21+
tags = (IPUWorkflowTag, FactsPhaseTag.Before)
22+
23+
def process(self):
24+
vendor_mapping_data = {}
25+
active_vendors = set()
26+
27+
# Make a dict for easy mapping of repoid -> corresponding vendor name.
28+
for vendor_src_repodata in api.consume(VendorSourceRepos):
29+
for vendor_src_repo in vendor_src_repodata.source_repoids:
30+
vendor_mapping_data[vendor_src_repo] = vendor_src_repodata.vendor
31+
32+
# Is the repo listed in the vendor map as from_repoid present on the system?
33+
for repos_facts in api.consume(RepositoriesFacts):
34+
for repo_file in repos_facts.repositories:
35+
for repo_data in repo_file.data:
36+
self.log.debug(
37+
"Looking for repository {} in vendor maps".format(repo_data.repoid)
38+
)
39+
if repo_data.enabled and repo_data.repoid in vendor_mapping_data:
40+
# If the vendor's repository is present in the system and enabled, count the vendor as active.
41+
new_vendor = vendor_mapping_data[repo_data.repoid]
42+
self.log.debug(
43+
"Repository {} found and enabled, enabling vendor {}".format(
44+
repo_data.repoid, new_vendor
45+
)
46+
)
47+
active_vendors.add(new_vendor)
48+
49+
if active_vendors:
50+
self.log.debug("Active vendor list: {}".format(active_vendors))
51+
api.produce(ActiveVendorList(data=list(active_vendors)))
52+
else:
53+
self.log.info("No active vendors found, vendor list not generated")

repos/system_upgrade/common/actors/filterrpmtransactionevents/actor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,29 @@ def process(self):
3232
to_remove = set()
3333
to_keep = set()
3434
to_upgrade = set()
35+
to_reinstall = set()
3536
modules_to_enable = {}
3637
modules_to_reset = {}
3738
for event in self.consume(RpmTransactionTasks, PESRpmTransactionTasks):
3839
local_rpms.update(event.local_rpms)
3940
to_install.update(event.to_install)
4041
to_remove.update(installed_pkgs.intersection(event.to_remove))
4142
to_keep.update(installed_pkgs.intersection(event.to_keep))
43+
to_reinstall.update(installed_pkgs.intersection(event.to_reinstall))
4244
modules_to_enable.update({'{}:{}'.format(m.name, m.stream): m for m in event.modules_to_enable})
4345
modules_to_reset.update({'{}:{}'.format(m.name, m.stream): m for m in event.modules_to_reset})
4446

4547
to_remove.difference_update(to_keep)
4648

4749
# run upgrade for the rest of RH signed pkgs which we do not have rule for
48-
to_upgrade = installed_pkgs - (to_install | to_remove)
50+
to_upgrade = installed_pkgs - (to_install | to_remove | to_reinstall)
4951

5052
self.produce(FilteredRpmTransactionTasks(
5153
local_rpms=list(local_rpms),
5254
to_install=list(to_install),
5355
to_remove=list(to_remove),
5456
to_keep=list(to_keep),
5557
to_upgrade=list(to_upgrade),
58+
to_reinstall=list(to_reinstall),
5659
modules_to_reset=list(modules_to_reset.values()),
5760
modules_to_enable=list(modules_to_enable.values())))

repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,10 @@ def _get_path_to_gpg_certs():
112112
# only beta is special in regards to the GPG signing keys
113113
if target_product_type == 'beta':
114114
certs_dir = '{}beta'.format(target_major_version)
115-
return os.path.join(api.get_common_folder_path(GPG_CERTS_FOLDER), certs_dir)
115+
return [
116+
"/etc/leapp/files/vendors.d/rpm-gpg/",
117+
os.path.join(api.get_common_folder_path(GPG_CERTS_FOLDER), certs_dir)
118+
]
116119

117120

118121
def _expand_vars(path):
@@ -169,14 +172,15 @@ def _get_pubkeys(installed_rpms):
169172
"""
170173
pubkeys = _pubkeys_from_rpms(installed_rpms)
171174
certs_path = _get_path_to_gpg_certs()
172-
for certname in os.listdir(certs_path):
173-
key_file = os.path.join(certs_path, certname)
174-
fps = _read_gpg_fp_from_file(key_file)
175-
if fps:
176-
pubkeys += fps
177-
# TODO: what about else: ?
178-
# The warning is now logged in _read_gpg_fp_from_file. We can raise
179-
# the priority of the message or convert it to report though.
175+
for trusted_dir in certs_path:
176+
for certname in os.listdir(trusted_dir):
177+
key_file = os.path.join(trusted_dir, certname)
178+
fps = _read_gpg_fp_from_file(key_file)
179+
if fps:
180+
pubkeys += fps
181+
# TODO: what about else: ?
182+
# The warning is now logged in _read_gpg_fp_from_file. We can raise
183+
# the priority of the message or convert it to report though.
180184
return pubkeys
181185

182186

@@ -270,11 +274,11 @@ def _report(title, summary, keys, inhibitor=False):
270274
)
271275
hint = (
272276
'Check the path to the listed GPG keys is correct, the keys are valid and'
273-
' import them into the host RPM DB or store them inside the {} directory'
277+
' import them into the host RPM DB or store them inside on of the {} directories'
274278
' prior the upgrade.'
275279
' If you want to proceed the in-place upgrade without checking any RPM'
276280
' signatures, execute leapp with the `--nogpgcheck` option.'
277-
.format(_get_path_to_gpg_certs())
281+
.format(','.format(_get_path_to_gpg_certs()))
278282
)
279283
groups = [reporting.Groups.REPOSITORY]
280284
if inhibitor:
@@ -305,8 +309,8 @@ def _report(title, summary, keys, inhibitor=False):
305309
def _report_missing_keys(keys):
306310
summary = (
307311
'Some of the target repositories require GPG keys that are not installed'
308-
' in the current RPM DB or are not stored in the {trust_dir} directory.'
309-
.format(trust_dir=_get_path_to_gpg_certs())
312+
' in the current RPM DB or are not stored in the {trust_dir} directories.'
313+
.format(trust_dir=','.join(_get_path_to_gpg_certs()))
310314
)
311315
_report('Detected unknown GPG keys for target system repositories', summary, keys, True)
312316

@@ -380,11 +384,12 @@ def _report_repos_missing_keys(repos):
380384

381385

382386
def register_dnfworkaround():
383-
api.produce(DNFWorkaround(
384-
display_name='import trusted gpg keys to RPM DB',
385-
script_path=api.current_actor().get_common_tool_path('importrpmgpgkeys'),
386-
script_args=[_get_path_to_gpg_certs()],
387-
))
387+
for trust_certs_dir in _get_path_to_gpg_certs():
388+
api.produce(DNFWorkaround(
389+
display_name='import trusted gpg keys to RPM DB',
390+
script_path=api.current_actor().get_common_tool_path('importrpmgpgkeys'),
391+
script_args=[trust_certs_dir],
392+
))
388393

389394

390395
@suppress_deprecation(TMPTargetRepositoriesFacts)

repos/system_upgrade/common/actors/peseventsscanner/actor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
RepositoriesMapping,
1111
RepositoriesSetupTasks,
1212
RHUIInfo,
13-
RpmTransactionTasks
13+
RpmTransactionTasks,
14+
ActiveVendorList,
1415
)
1516
from leapp.reporting import Report
1617
from leapp.tags import FactsPhaseTag, IPUWorkflowTag
@@ -33,6 +34,7 @@ class PesEventsScanner(Actor):
3334
RepositoriesMapping,
3435
RHUIInfo,
3536
RpmTransactionTasks,
37+
ActiveVendorList,
3638
)
3739
produces = (ConsumedDataAsset, PESRpmTransactionTasks, RepositoriesSetupTasks, Report)
3840
tags = (IPUWorkflowTag, FactsPhaseTag)

repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_event_parsing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class Action(IntEnum):
5858
MERGED = 5
5959
MOVED = 6
6060
RENAMED = 7
61+
REINSTALLED = 8
6162

6263

6364
def get_pes_events(pes_json_directory, pes_json_filename):
@@ -69,13 +70,14 @@ def get_pes_events(pes_json_directory, pes_json_filename):
6970
try:
7071
events_data = fetch.load_data_asset(api.current_actor(),
7172
pes_json_filename,
73+
asset_directory=pes_json_directory,
7274
asset_fulltext_name='PES events file',
7375
docs_url='',
7476
docs_title='')
7577
if not events_data:
7678
return None
7779

78-
if not events_data.get('packageinfo'):
80+
if events_data.get('packageinfo') is None:
7981
raise ValueError('Found PES data with invalid structure')
8082

8183
all_events = list(chain(*[parse_entry(entry) for entry in events_data['packageinfo']]))

repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from collections import defaultdict, namedtuple
22
from functools import partial
3+
import os
34

45
from leapp import reporting
56
from leapp.exceptions import StopActorExecutionError
67
from leapp.libraries.actor import peseventsscanner_repomap
78
from leapp.libraries.actor.pes_event_parsing import Action, get_pes_events, Package
89
from leapp.libraries.common.config import version
10+
from leapp.libraries.common.repomaputils import combine_repomap_messages
911
from leapp.libraries.stdlib import api
1012
from leapp.libraries.stdlib.config import is_verbose
1113
from leapp.models import (
@@ -19,7 +21,8 @@
1921
RepositoriesMapping,
2022
RepositoriesSetupTasks,
2123
RHUIInfo,
22-
RpmTransactionTasks
24+
RpmTransactionTasks,
25+
ActiveVendorList,
2326
)
2427

2528
SKIPPED_PKGS_MSG = (
@@ -30,8 +33,9 @@
3033
'for details.\nThe list of these packages:'
3134
)
3235

36+
VENDORS_DIR = "/etc/leapp/files/vendors.d"
3337

34-
TransactionConfiguration = namedtuple('TransactionConfiguration', ('to_install', 'to_remove', 'to_keep'))
38+
TransactionConfiguration = namedtuple('TransactionConfiguration', ('to_install', 'to_remove', 'to_keep', 'to_reinstall'))
3539

3640

3741
def get_cloud_provider_name(cloud_provider_variant):
@@ -82,14 +86,15 @@ def get_transaction_configuration():
8286
These configuration files have higher priority than PES data.
8387
:return: RpmTransactionTasks model instance
8488
"""
85-
transaction_configuration = TransactionConfiguration(to_install=[], to_remove=[], to_keep=[])
89+
transaction_configuration = TransactionConfiguration(to_install=[], to_remove=[], to_keep=[], to_reinstall=[])
8690

8791
_Pkg = partial(Package, repository=None, modulestream=None)
8892

8993
for tasks in api.consume(RpmTransactionTasks):
9094
transaction_configuration.to_install.extend(_Pkg(name=pkg_name) for pkg_name in tasks.to_install)
9195
transaction_configuration.to_remove.extend(_Pkg(name=pkg_name) for pkg_name in tasks.to_remove)
9296
transaction_configuration.to_keep.extend(_Pkg(name=pkg_name) for pkg_name in tasks.to_keep)
97+
transaction_configuration.to_reinstall.extend(_Pkg(name=pkg_name) for pkg_name in tasks.to_reinstall)
9398
return transaction_configuration
9499

95100

@@ -129,6 +134,7 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
129134
logger = api.current_logger()
130135
# Start with the installed packages and modify the set according to release events
131136
target_pkgs = set(source_installed_pkgs)
137+
pkgs_to_reinstall = set()
132138

133139
release_events = [e for e in events if e.to_release == release]
134140

@@ -165,9 +171,12 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
165171
target_pkgs = target_pkgs.difference(event.in_pkgs)
166172
target_pkgs = target_pkgs.union(event.out_pkgs)
167173

174+
if (event.action == Action.REINSTALLED and is_any_in_pkg_present):
175+
pkgs_to_reinstall = pkgs_to_reinstall.union(event.in_pkgs)
176+
168177
pkgs_to_demodularize = pkgs_to_demodularize.difference(event.in_pkgs)
169178

170-
return (target_pkgs, pkgs_to_demodularize)
179+
return (target_pkgs, pkgs_to_demodularize, pkgs_to_reinstall)
171180

172181

173182
def remove_undesired_events(events, relevant_to_releases):
@@ -233,15 +242,17 @@ def compute_packages_on_target_system(source_pkgs, events, releases):
233242
did_processing_cross_major_version = True
234243
pkgs_to_demodularize = {pkg for pkg in target_pkgs if pkg.modulestream}
235244

236-
target_pkgs, pkgs_to_demodularize = compute_pkg_changes_between_consequent_releases(target_pkgs, events,
237-
release, seen_pkgs,
238-
pkgs_to_demodularize)
245+
target_pkgs, pkgs_to_demodularize, pkgs_to_reinstall = compute_pkg_changes_between_consequent_releases(
246+
target_pkgs, events,
247+
release, seen_pkgs,
248+
pkgs_to_demodularize
249+
)
239250
seen_pkgs = seen_pkgs.union(target_pkgs)
240251

241252
demodularized_pkgs = {Package(pkg.name, pkg.repository, None) for pkg in pkgs_to_demodularize}
242253
demodularized_target_pkgs = target_pkgs.difference(pkgs_to_demodularize).union(demodularized_pkgs)
243254

244-
return (demodularized_target_pkgs, pkgs_to_demodularize)
255+
return (demodularized_target_pkgs, pkgs_to_demodularize, pkgs_to_reinstall)
245256

246257

247258
def compute_rpm_tasks_from_pkg_set_diff(source_pkgs, target_pkgs, pkgs_to_demodularize):
@@ -345,15 +356,13 @@ def get_pesid_to_repoid_map(target_pesids):
345356
:return: Dictionary mapping the target_pesids to their corresponding repoid
346357
"""
347358

348-
repositories_map_msgs = api.consume(RepositoriesMapping)
349-
repositories_map_msg = next(repositories_map_msgs, None)
350-
if list(repositories_map_msgs):
351-
api.current_logger().warning('Unexpectedly received more than one RepositoriesMapping message.')
352-
if not repositories_map_msg:
359+
repositories_map_msgs = list(api.consume(RepositoriesMapping))
360+
if not repositories_map_msgs:
353361
raise StopActorExecutionError(
354362
'Cannot parse RepositoriesMapping data properly',
355363
details={'Problem': 'Did not receive a message with mapped repositories'}
356364
)
365+
repositories_map_msg = combine_repomap_messages(repositories_map_msgs)
357366

358367
rhui_info = next(api.consume(RHUIInfo), RHUIInfo(provider=''))
359368

@@ -485,6 +494,19 @@ def process():
485494
if not events:
486495
return
487496

497+
active_vendors = []
498+
for vendor_list in api.consume(ActiveVendorList):
499+
active_vendors.extend(vendor_list.data)
500+
501+
pes_json_suffix = "_pes.json"
502+
if os.path.isdir(VENDORS_DIR):
503+
vendor_pesfiles = list(filter(lambda vfile: pes_json_suffix in vfile, os.listdir(VENDORS_DIR)))
504+
505+
for pesfile in vendor_pesfiles:
506+
if pesfile[:-len(pes_json_suffix)] in active_vendors:
507+
vendor_events = get_pes_events(VENDORS_DIR, pesfile)
508+
events.extend(vendor_events)
509+
488510
releases = get_relevant_releases(events)
489511
source_pkgs = get_installed_pkgs()
490512
source_pkgs = apply_transaction_configuration(source_pkgs)
@@ -496,7 +518,7 @@ def process():
496518
events = remove_undesired_events(events, releases)
497519

498520
# Apply events - compute what packages should the target system have
499-
target_pkgs, pkgs_to_demodularize = compute_packages_on_target_system(source_pkgs, events, releases)
521+
target_pkgs, pkgs_to_demodularize, pkgs_to_reinstall = compute_packages_on_target_system(source_pkgs, events, releases)
500522

501523
# Packages coming out of the events have PESID as their repository, however, we need real repoid
502524
target_pkgs = replace_pesids_with_repoids_in_packages(target_pkgs, repoids_of_source_pkgs)
@@ -512,4 +534,5 @@ def process():
512534
# Compare the packages on source system and the computed packages on target system and determine what to install
513535
rpm_tasks = compute_rpm_tasks_from_pkg_set_diff(source_pkgs, target_pkgs, pkgs_to_demodularize)
514536
if rpm_tasks:
537+
rpm_tasks.to_reinstall = sorted(pkgs_to_reinstall)
515538
api.produce(rpm_tasks)

0 commit comments

Comments
 (0)