Add support for KojiResolver in component reuse code.

In this commit, when component reuse code finds out that the base module uses
KojiResolver, it uses the `KojiResolver.get_buildrequired_modules` method
to find out possible modules to reuse and limits the original query just
by the IDs of these modules.

In order to do that, this commit splits the original
`KojiResolver.get_buildrequired_modulemds` into two methods:

- The `get_buildrequired_modules` returning the ModuleBuilds.
- The `get_buildrequired_modulemds` calling the `get_buildrequired_modules`
  and returning modulemd metadata.
This commit is contained in:
Jan Kaluza
2019-10-02 15:42:25 +02:00
parent fc39df9487
commit 486dc39898
6 changed files with 208 additions and 51 deletions

View File

@@ -80,24 +80,21 @@ class KojiResolver(DBResolver):
return result
def get_buildrequired_modulemds(self, name, stream, base_module_mmd):
def get_buildrequired_modules(self, name, stream, base_module_mmd):
"""
Returns modulemd metadata of all module builds with `name` and `stream` which are tagged
Returns ModuleBuild objects of all module builds with `name` and `stream` which are tagged
in the Koji tag defined in `base_module_mmd`.
:param str name: Name of module to return.
:param str stream: Stream of module to return.
:param Modulemd base_module_mmd: Base module metadata.
:return list: List of modulemd metadata.
:return list: List of ModuleBuilds.
"""
# Get the `koji_tag_with_modules`. If the `koji_tag_with_modules` is not configured for
# the base module, fallback to DBResolver.
tag = base_module_mmd.get_xmd().get("mbs", {}).get("koji_tag_with_modules")
if not tag:
log.info(
"The %s does not define 'koji_tag_with_modules'. Falling back to DBResolver." % (
base_module_mmd.get_nsvc()))
return DBResolver.get_buildrequired_modulemds(self, name, stream, base_module_mmd)
return []
# Create KojiSession. We need to import here because of circular dependencies.
from module_build_service.builder.KojiModuleBuilder import KojiModuleBuilder
@@ -146,9 +143,8 @@ class KojiResolver(DBResolver):
latest_builds += list(nsv_builds)
break
# For each latest module build, find the matching ModuleBuild and store its modulemd
# in `mmds`.
mmds = []
# For each latest module build, find the matching ModuleBuild and store it into `ret`.
ret = []
for build in latest_builds:
version, context = build["release"].split(".")
module = models.ModuleBuild.get_build_from_nsvc(
@@ -157,9 +153,29 @@ class KojiResolver(DBResolver):
raise ValueError(
"Module %s is tagged in the %s Koji tag, but does not exist "
"in MBS DB." % (":".join([name, stream, version, context]), tag))
mmds.append(module.mmd())
ret.append(module)
return mmds
return ret
def get_buildrequired_modulemds(self, name, stream, base_module_mmd):
"""
Returns modulemd metadata of all module builds with `name` and `stream` which are tagged
in the Koji tag defined in `base_module_mmd`.
:param str name: Name of module to return.
:param str stream: Stream of module to return.
:param Modulemd base_module_mmd: Base module metadata.
:return list: List of modulemd metadata.
"""
tag = base_module_mmd.get_xmd().get("mbs", {}).get("koji_tag_with_modules")
if not tag:
log.info(
"The %s does not define 'koji_tag_with_modules'. Falling back to DBResolver." %
(base_module_mmd.get_nsvc()))
return DBResolver.get_buildrequired_modulemds(self, name, stream, base_module_mmd)
modules = self.get_buildrequired_modules(name, stream, base_module_mmd)
return [module.mmd() for module in modules]
def get_compatible_base_module_modulemds(self, *args, **kwargs):
"""

View File

@@ -251,6 +251,10 @@ def get_base_module_mmds(db_session, mmd):
continue
stream_mmd = mmds[0]
# Add the module to `seen` and `ret`.
seen.add(ns)
ret["ready"].append(stream_mmd)
# Get the list of compatible virtual streams.
xmd = stream_mmd.get_xmd()
virtual_streams = xmd.get("mbs", {}).get("virtual_streams")
@@ -259,8 +263,6 @@ def get_base_module_mmds(db_session, mmd):
# it is clear that there are no compatible streams, so return just this
# `stream_mmd`.
if not virtual_streams:
seen.add(ns)
ret["ready"].append(stream_mmd)
continue
virtual_streams = xmd["mbs"]["virtual_streams"]

View File

@@ -27,6 +27,7 @@ import kobo.rpmlib
import module_build_service.messaging
from module_build_service import log, models, conf
from module_build_service.utils.mse import get_base_module_mmds
from module_build_service.resolver import GenericResolver
def reuse_component(component, previous_component_build, change_state_now=False):
@@ -93,6 +94,22 @@ def get_reusable_module(db_session, module):
mmd = module.mmd()
previous_module_build = None
# The `base_mmds` will contain the list of base modules against which the possible modules
# to reuse are built. There are three options how these base modules are found:
#
# 1) The `conf.allow_only_compatible_base_modules` is False. This means that MBS should
# not try to find any compatible base modules in its DB and simply use the buildrequired
# base module as it is.
# 2) The `conf.allow_only_compatible_base_modules` is True and DBResolver is used. This means
# that MBS should try to find the compatible modules using its database.
# The `get_base_module_mmds` finds out the list of compatible modules and returns mmds of
# all of them.
# 3) The `conf.allow_only_compatible_base_modules` is True and KojiResolver is used. This
# means that MBS should *not* try to find any compatible base modules in its DB, but
# instead just query Koji using KojiResolver later to find out the module to
# reuse. The list of compatible base modules is defined by Koji tag inheritance directly
# in Koji.
# The `get_base_module_mmds` in this case returns just the buildrequired base module.
if conf.allow_only_compatible_base_modules:
log.debug("Checking for compatible base modules")
base_mmds = get_base_module_mmds(db_session, mmd)["ready"]
@@ -108,22 +125,43 @@ def get_reusable_module(db_session, module):
base_mmds.append(br.mmd())
for base_mmd in base_mmds:
mbs_xmd = mmd.get_xmd()["mbs"]
if base_mmd.get_module_name() not in mbs_xmd["buildrequires"]:
continue
mbs_xmd["buildrequires"][base_mmd.get_module_name()]["stream"] \
= base_mmd.get_stream_name()
build_context = module.calculate_build_context(mbs_xmd["buildrequires"])
# Find the latest module that is in the ready state
previous_module_build = (
db_session.query(models.ModuleBuild)
.filter_by(name=mmd.get_module_name())
.filter_by(stream=mmd.get_stream_name())
.filter_by(state=models.BUILD_STATES["ready"])
.filter(models.ModuleBuild.scmurl.isnot(None))
.filter_by(build_context=build_context)
.order_by(models.ModuleBuild.time_completed.desc()))
koji_resolver_enabled = base_mmd.get_xmd().get("mbs", {}).get("koji_tag_with_modules")
if koji_resolver_enabled:
# Find ModuleBuilds tagged in the Koji tag using KojiResolver.
resolver = GenericResolver.create(db_session, conf, backend="koji")
possible_modules_to_reuse = resolver.get_buildrequired_modules(
module.name, module.stream, base_mmd)
# Limit the query to these modules.
possible_module_ids = [m.id for m in possible_modules_to_reuse]
previous_module_build = previous_module_build.filter(
models.ModuleBuild.id.in_(possible_module_ids))
# Limit the query to modules sharing the same `build_context_no_bms`. That means they
# have the same buildrequirements.
previous_module_build = previous_module_build.filter_by(
build_context_no_bms=module.build_context_no_bms)
else:
# Recompute the build_context with compatible base module stream.
mbs_xmd = mmd.get_xmd()["mbs"]
if base_mmd.get_module_name() not in mbs_xmd["buildrequires"]:
previous_module_build = None
continue
mbs_xmd["buildrequires"][base_mmd.get_module_name()]["stream"] \
= base_mmd.get_stream_name()
build_context = module.calculate_build_context(mbs_xmd["buildrequires"])
# Limit the query to find only modules sharing the same build_context.
previous_module_build = previous_module_build.filter_by(build_context=build_context)
# If we are rebuilding with the "changed-and-after" option, then we can't reuse
# components from modules that were built more liberally
if module.rebuild_strategy == "changed-and-after":