modularity: depsolving
The commit implements modularity for the dnf4 dependency solver. The dependency solver now takes two new keys per transaction: `module-install-specs` and `module-enable-specs`, each of which is a list of module specifications. When modules are used and packages for them are installed a new dictionary is returned in the response where each key is the name of an enabled module and the value are the contents of the module configuration and the module failsafe file that DNF needs in the resulting system. These values can be used by consumers of the dependency solver, such as `images`, to create the appropriate inline sources and copy stages. Signed-off-by: Simon de Vlieger <supakeen@redhat.com>
This commit is contained in:
parent
1a529d78a9
commit
6e9fb7dd6a
1 changed files with 80 additions and 1 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import itertools
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
import textwrap
|
||||
from datetime import datetime
|
||||
from typing import Dict, List
|
||||
|
||||
|
|
@ -54,6 +56,7 @@ class DNF(SolverBase):
|
|||
self.base.conf.substitutions['arch'] = arch
|
||||
self.base.conf.substitutions['basearch'] = dnf.rpm.basearch(arch)
|
||||
self.base.conf.substitutions['releasever'] = releasever
|
||||
|
||||
if hasattr(self.base.conf, "optional_metadata_types"):
|
||||
# the attribute doesn't exist on older versions of dnf; ignore the option when not available
|
||||
self.base.conf.optional_metadata_types.extend(arguments.get("optional-metadata", []))
|
||||
|
|
@ -85,6 +88,10 @@ class DNF(SolverBase):
|
|||
except dnf.exceptions.Error as e:
|
||||
raise RepoError(e) from e
|
||||
|
||||
# enable module resolving
|
||||
self.base_module = dnf.module.module_base.ModuleBase(self.base)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
@staticmethod
|
||||
def _dnfrepo(desc, parent_conf=None):
|
||||
|
|
@ -261,7 +268,14 @@ class DNF(SolverBase):
|
|||
for installed_pkg in last_transaction:
|
||||
self.base.package_install(installed_pkg, strict=True)
|
||||
|
||||
# depsolve the current transaction
|
||||
# enabling a module means that packages can be installed from that
|
||||
# module
|
||||
self.base_module.enable(transaction.get("module-enable-specs", []))
|
||||
|
||||
# installing a module takes the specification of the module and then
|
||||
# installs all packages belonging to it
|
||||
self.base_module.install(transaction.get("module-install-specs", []))
|
||||
|
||||
self.base.install_specs(
|
||||
transaction.get("package-specs"),
|
||||
transaction.get("exclude-specs"),
|
||||
|
|
@ -288,6 +302,7 @@ class DNF(SolverBase):
|
|||
pkg_repos = {}
|
||||
for package in last_transaction:
|
||||
packages.append({
|
||||
"nevra": f"{package.name}-{package.evr}.{package.arch}",
|
||||
"name": package.name,
|
||||
"epoch": package.epoch,
|
||||
"version": package.version,
|
||||
|
|
@ -322,9 +337,73 @@ class DNF(SolverBase):
|
|||
"solver": "dnf",
|
||||
"packages": packages,
|
||||
"repos": repositories,
|
||||
"modules": {},
|
||||
}
|
||||
|
||||
if "sbom" in arguments:
|
||||
response["sbom"] = self._sbom_for_pkgset(last_transaction)
|
||||
|
||||
# if any modules have been requested we add sources for these so they can
|
||||
# be used by stages to enable the modules in the eventual artifact
|
||||
modules = []
|
||||
|
||||
for transaction in transactions:
|
||||
if transaction.get("module-install-specs") or transaction.get("module-enable-specs"):
|
||||
# we'll be checking later if any packages-from-modules are in the
|
||||
# packages-to-install set so let's do this only once here
|
||||
package_nevras = list(p["nevra"] for p in packages)
|
||||
|
||||
for module_spec in itertools.chain(
|
||||
transaction.get("module-install-specs", []),
|
||||
transaction.get("module-enable-specs", []),
|
||||
):
|
||||
# we don't particularly care about the NSVCAP here, just the module
|
||||
# packages that were previously selected
|
||||
module_packages, _ = self.base_module.get_modules(module_spec)
|
||||
|
||||
# we now need to do an annoying dance as multiple modules could be
|
||||
# returned by `.get_modules`, we need to select the *same* one as
|
||||
# previously selected. we do this by checking if any of the module
|
||||
# packages are in the packages set marked for installation.
|
||||
|
||||
# this is a result of not being able to get the enabled modules
|
||||
# from the transaction, if that turns out to be possible then
|
||||
# we can get rid of these shenanigans
|
||||
for module_package in module_packages:
|
||||
module_nevras = module_package.getArtifacts()
|
||||
|
||||
if any(module_nevra in package_nevras for module_nevra in module_nevras):
|
||||
# a package from this module is being installed so we must
|
||||
# use this module
|
||||
modules.append(module_package)
|
||||
|
||||
# we are probably able to skip the rest of the `module_packages`
|
||||
# here if we want since no two modules can be enabled by the same
|
||||
# name
|
||||
break
|
||||
|
||||
# now we have the information we need about modules so we need to return *some*
|
||||
# information to who is using the depsolver so they can use that information to
|
||||
# enable these modules in the artifact
|
||||
|
||||
# there are two files that matter for each module that is used, the caller needs
|
||||
# to write a file to `/etc/dnf/modules.d/{module_name}.module` to enable the
|
||||
# module for dnf
|
||||
|
||||
# the caller also needs to set up `/var/lib/dnf/modulefailsafe/` with the contents
|
||||
# of the modulemd for the selected modules, this is to ensure that even when a
|
||||
# repository is disabled or disappears that non-modular content can't be installed
|
||||
# see: https://dnf.readthedocs.io/en/latest/modularity.html#fail-safe-mechanisms
|
||||
for module in modules:
|
||||
response["modules"][module.getName()] = {
|
||||
"module-file": textwrap.dedent(f"""\
|
||||
[{module.getName()}]
|
||||
name={module.getName()}
|
||||
stream={module.getStream()}
|
||||
profiles=common
|
||||
state=enabled
|
||||
"""),
|
||||
"failsafe-file": module.getYaml(),
|
||||
}
|
||||
|
||||
return response
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue