mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-04-13 16:29:49 +08:00
Resolve stream collision with modules added to ursine content
This resolve the stream collision by adding specific RPMs to module-build-macros SRPM as Conflicts. For more information about module stream collision, please refer to docstring in utils/ursine.py Signed-off-by: Chenxiong Qi <cqi@redhat.com>
This commit is contained in:
@@ -41,8 +41,10 @@ except ImportError:
|
||||
import xmlrpc.client as xmlrpclib
|
||||
|
||||
import munch
|
||||
from itertools import chain
|
||||
from OpenSSL.SSL import SysCallError
|
||||
|
||||
|
||||
from module_build_service import log, conf, models
|
||||
import module_build_service.scm
|
||||
import module_build_service.utils
|
||||
@@ -287,6 +289,12 @@ class KojiModuleBuilder(GenericBuilder):
|
||||
break
|
||||
return filtered_rpms
|
||||
|
||||
@staticmethod
|
||||
def format_conflicts_line(nevr):
|
||||
"""Helper method to format a Conflicts line with a RPM N-E:V-R"""
|
||||
parsed_nvr = kobo.rpmlib.parse_nvr(nevr)
|
||||
return "Conflicts: {name} = {epoch}:{version}-{release}".format(**parsed_nvr)
|
||||
|
||||
@staticmethod
|
||||
def get_disttag_srpm(disttag, module_build):
|
||||
|
||||
@@ -304,22 +312,29 @@ class KojiModuleBuilder(GenericBuilder):
|
||||
# build-requires based on their "mmd.filter.rpms". So we set the
|
||||
# module-build-macros to conflict with these filtered RPMs to ensure
|
||||
# they won't be installed to buildroot.
|
||||
filter_conflicts = ""
|
||||
filter_conflicts = []
|
||||
for req_name, req_data in mmd.get_xmd()["mbs"]["buildrequires"].items():
|
||||
if req_data["filtered_rpms"]:
|
||||
filter_conflicts += "# Filtered rpms from %s module:\n" % (
|
||||
req_name)
|
||||
filter_conflicts.append("# Filtered rpms from %s module:" % req_name)
|
||||
# Check if the module depends on itself
|
||||
if req_name == module_build.name:
|
||||
filtered_rpms = KojiModuleBuilder._get_filtered_rpms_on_self_dep(
|
||||
module_build, req_data["filtered_rpms"])
|
||||
else:
|
||||
filtered_rpms = req_data["filtered_rpms"]
|
||||
for nvr in filtered_rpms:
|
||||
parsed_nvr = kobo.rpmlib.parse_nvr(nvr)
|
||||
filter_conflicts += "Conflicts: %s = %s:%s-%s\n" % (
|
||||
parsed_nvr["name"], parsed_nvr["epoch"],
|
||||
parsed_nvr["version"], parsed_nvr["release"])
|
||||
filter_conflicts.extend(map(
|
||||
KojiModuleBuilder.format_conflicts_line, filtered_rpms))
|
||||
|
||||
if req_name in conf.base_module_names and 'ursine_rpms' in req_data:
|
||||
comments = (
|
||||
'# Filter out RPMs from stream collision modules found from ursine content'
|
||||
' for base module {}:'.format(req_name),
|
||||
'# ' + ', '.join(req_data['stream_collision_modules']),
|
||||
)
|
||||
filter_conflicts.extend(chain(
|
||||
comments,
|
||||
map(KojiModuleBuilder.format_conflicts_line, req_data['ursine_rpms'])
|
||||
))
|
||||
|
||||
spec_content = """
|
||||
%global dist {disttag}
|
||||
@@ -373,7 +388,7 @@ chmod 644 %buildroot/etc/rpm/macros.zz-modules
|
||||
module_stream=module_build.stream,
|
||||
module_version=module_build.version,
|
||||
module_context=module_build.context,
|
||||
filter_conflicts=filter_conflicts)
|
||||
filter_conflicts='\n'.join(filter_conflicts))
|
||||
|
||||
modulemd_macros = ""
|
||||
rpm_buildopts = mmd.get_rpm_buildopts()
|
||||
|
||||
@@ -366,6 +366,11 @@ class ModuleBuild(MBSBase):
|
||||
sqlalchemy.cast(ModuleBuild.version, db.BigInteger) == subq.c.maxversion))
|
||||
return query.all()
|
||||
|
||||
@staticmethod
|
||||
def get_build_by_koji_tag(session, tag):
|
||||
"""Get build by its koji_tag"""
|
||||
return session.query(ModuleBuild).filter_by(koji_tag=tag).first()
|
||||
|
||||
def mmd(self):
|
||||
try:
|
||||
mmd = Modulemd.Module().new_from_string(self.modulemd)
|
||||
|
||||
@@ -306,3 +306,8 @@ class DBResolver(GenericResolver):
|
||||
}
|
||||
|
||||
return new_requires
|
||||
|
||||
def get_modulemd_by_koji_tag(self, tag):
|
||||
with models.make_session(self.config) as session:
|
||||
module = models.ModuleBuild.get_build_by_koji_tag(session, tag)
|
||||
return module.mmd() if module else None
|
||||
|
||||
@@ -394,3 +394,12 @@ class MBSResolver(GenericResolver):
|
||||
import_mmd(db.session, mmd)
|
||||
|
||||
return new_requires
|
||||
|
||||
def get_modulemd_by_koji_tag(self, tag):
|
||||
resp = self.session.get(self.mbs_prod_url, params={'koji_tag': tag, 'verbose': True})
|
||||
data = resp.json()
|
||||
if data['items']:
|
||||
modulemd = data['items'][0]['modulemd']
|
||||
return self.extract_modulemd(modulemd)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -115,3 +115,13 @@ class GenericResolver(six.with_metaclass(ABCMeta)):
|
||||
@abstractmethod
|
||||
def resolve_requires(self, requires):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def get_modulemd_by_koji_tag(self, tag):
|
||||
"""Get module metadata by module's koji_tag
|
||||
|
||||
:param str tag: name of module's koji_tag.
|
||||
:return: module metadata
|
||||
:rtype: Modulemd.Module
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -27,3 +27,4 @@ from module_build_service.utils.views import * # noqa
|
||||
from module_build_service.utils.reuse import * # noqa
|
||||
from module_build_service.utils.submit import * # noqa
|
||||
from module_build_service.utils.batches import * # noqa
|
||||
from module_build_service.utils.ursine import * # noqa
|
||||
|
||||
@@ -42,47 +42,30 @@ from module_build_service.errors import (
|
||||
ValidationError, UnprocessableEntity, Forbidden, Conflict)
|
||||
from module_build_service import glib
|
||||
from .mse import generate_expanded_mmds
|
||||
from module_build_service.utils.ursine import record_stream_collision_modules
|
||||
|
||||
|
||||
def record_filtered_rpms(mmd):
|
||||
"""
|
||||
Reads the mmd["xmd"]["buildrequires"] and extends it with "filtered_rpms"
|
||||
list containing the NVRs of filtered RPMs in a buildrequired module.
|
||||
"""Record filtered RPMs that should not be installed into buildroot
|
||||
|
||||
:param Modulemd mmd: Modulemd of input module.
|
||||
:rtype: Modulemd
|
||||
These RPMs are filtered:
|
||||
|
||||
* Reads the mmd["xmd"]["buildrequires"] and extends it with "filtered_rpms"
|
||||
list containing the NVRs of filtered RPMs in a buildrequired module.
|
||||
|
||||
* Every base module could have stream collision modules, whose built RPMs
|
||||
should be filtered out to avoid collision. The filtered out RPMs will be
|
||||
stored into the base module as well.
|
||||
|
||||
:param Modulemd mmd: Modulemd that will be built next.
|
||||
:rtype: Modulemd.Module
|
||||
:return: Modulemd extended with the "filtered_rpms" in XMD section.
|
||||
"""
|
||||
# Imported here to allow import of utils in GenericBuilder.
|
||||
from module_build_service.builder import GenericBuilder
|
||||
|
||||
new_buildrequires = {}
|
||||
resolver = module_build_service.resolver.system_resolver
|
||||
for req_name, req_data in mmd.get_xmd()["mbs"]["buildrequires"].items():
|
||||
# In case this is module resubmit or local build, the filtered_rpms
|
||||
# will already be there, so there is no point in generating them again.
|
||||
if "filtered_rpms" in req_data:
|
||||
new_buildrequires[req_name] = req_data
|
||||
continue
|
||||
|
||||
# We can just get the first modulemd data from result right here thanks to
|
||||
# strict=True, so in case the module cannot be found, get_module_modulemds
|
||||
# raises an exception.
|
||||
req_mmd = resolver.get_module_modulemds(
|
||||
req_name, req_data["stream"], req_data["version"], req_data["context"], True)[0]
|
||||
|
||||
# Find out the particular NVR of filtered packages
|
||||
filtered_rpms = []
|
||||
rpm_filter = req_mmd.get_rpm_filter()
|
||||
if rpm_filter and rpm_filter.get():
|
||||
rpm_filter = rpm_filter.get()
|
||||
built_nvrs = GenericBuilder.backends[conf.system].get_built_rpms_in_module_build(
|
||||
req_mmd)
|
||||
for nvr in built_nvrs:
|
||||
parsed_nvr = kobo.rpmlib.parse_nvr(nvr)
|
||||
if parsed_nvr["name"] in rpm_filter:
|
||||
filtered_rpms.append(nvr)
|
||||
req_data["filtered_rpms"] = filtered_rpms
|
||||
_record_filtered_rpms(req_name, req_data)
|
||||
if req_name in conf.base_module_names and 'stream_collision_modules' in req_data:
|
||||
_record_ursine_rpms(req_data)
|
||||
new_buildrequires[req_name] = req_data
|
||||
|
||||
# Replace the old buildrequires with new ones.
|
||||
@@ -92,6 +75,85 @@ def record_filtered_rpms(mmd):
|
||||
return mmd
|
||||
|
||||
|
||||
def _record_filtered_rpms(req_name, req_data):
|
||||
"""Store filtered RPMs by a buildrequire module
|
||||
|
||||
This methods looks for key ``filtered_rpms`` in a buildrequired module's
|
||||
metadata. If there is, nothing is done, just keep it unchanged, which case
|
||||
could be a module is resubmitted or a local build.
|
||||
|
||||
Otherwise, ``_record_filtered_rpms`` will get module's RPMs listed in
|
||||
filter section, then store them with the key into metadata in place.
|
||||
|
||||
:param str req_name: name of a buildrequired module.
|
||||
:param dict req_data: a mapping containing metadata of the buildrequired
|
||||
module. A pair of ``req_name`` and ``req_data`` is usually the one of
|
||||
xmd.mbs.buildrequires, which are stored during the module stream
|
||||
expansion process. At least, ``req_data`` must have keys stream,
|
||||
version and context. Key ``filtered_rpms`` will be added as a list of
|
||||
RPMs N-E:V-R.
|
||||
"""
|
||||
# Imported here to allow import of utils in GenericBuilder.
|
||||
from module_build_service.builder import GenericBuilder
|
||||
|
||||
# In case this is module resubmit or local build, the filtered_rpms
|
||||
# will already be there, so there is no point in generating them again.
|
||||
if "filtered_rpms" in req_data:
|
||||
return
|
||||
|
||||
resolver = module_build_service.resolver.GenericResolver.create(conf)
|
||||
|
||||
# We can just get the first modulemd data from result right here thanks to
|
||||
# strict=True, so in case the module cannot be found, get_module_modulemds
|
||||
# raises an exception.
|
||||
req_mmd = resolver.get_module_modulemds(
|
||||
req_name, req_data["stream"], req_data["version"], req_data["context"], True)[0]
|
||||
|
||||
# Find out the particular NVR of filtered packages
|
||||
filtered_rpms = []
|
||||
rpm_filter = req_mmd.get_rpm_filter()
|
||||
if rpm_filter and rpm_filter.get():
|
||||
rpm_filter = rpm_filter.get()
|
||||
built_nvrs = GenericBuilder.backends[conf.system].get_built_rpms_in_module_build(
|
||||
req_mmd)
|
||||
for nvr in built_nvrs:
|
||||
parsed_nvr = kobo.rpmlib.parse_nvr(nvr)
|
||||
if parsed_nvr["name"] in rpm_filter:
|
||||
filtered_rpms.append(nvr)
|
||||
req_data["filtered_rpms"] = filtered_rpms
|
||||
|
||||
|
||||
def _record_ursine_rpms(req_data):
|
||||
"""Store ursine RPMs
|
||||
|
||||
This method handles key ``stream_collision_modules`` from a buildrequired
|
||||
module's metadata to find out all built RPMs and then store them with a new
|
||||
key ``ursine_rpms`` into metadata in place.
|
||||
|
||||
:param dict req_data: a mapping containing metadata of the buildrequired
|
||||
module. At least, ``req_data`` must have keys stream, version and
|
||||
context. As a result, a new key ``filtered_ursine_rpms`` will be added
|
||||
with a list of RPMs N-E:V-R which are built for the found stream
|
||||
collision modules.
|
||||
"""
|
||||
from module_build_service.builder import GenericBuilder
|
||||
resolver = module_build_service.resolver.GenericResolver.create(conf)
|
||||
|
||||
# Key stream_collision_modules is not used after rpms are recorded, but
|
||||
# just keep it here in case it would be helpful in the future.
|
||||
modules_nsvc = req_data['stream_collision_modules']
|
||||
get_built_rpms = GenericBuilder.backends[conf.system].get_built_rpms_in_module_build
|
||||
built_rpms = []
|
||||
|
||||
for nsvc in modules_nsvc:
|
||||
name, stream, version, context = nsvc.split(':')
|
||||
mmd = resolver.get_module_modulemds(
|
||||
name, stream, version, context, True)[0]
|
||||
built_rpms.extend(get_built_rpms(mmd))
|
||||
|
||||
req_data['ursine_rpms'] = built_rpms
|
||||
|
||||
|
||||
def _scm_get_latest(pkg):
|
||||
try:
|
||||
# If the modulemd specifies that the 'f25' branch is what
|
||||
@@ -363,21 +425,24 @@ def record_component_builds(mmd, module, initial_batch=1,
|
||||
return batch
|
||||
|
||||
|
||||
def submit_module_build_from_yaml(username, handle, stream=None, skiptests=False,
|
||||
optional_params=None):
|
||||
yaml_file = handle.read()
|
||||
mmd = load_mmd(yaml_file)
|
||||
|
||||
def mmd_set_default_nsv(mmd, filename, stream=None):
|
||||
# Mimic the way how default values are generated for modules that are stored in SCM
|
||||
# We can take filename as the module name as opposed to repo name,
|
||||
# and also we can take numeric representation of current datetime
|
||||
# as opposed to datetime of the last commit
|
||||
dt = datetime.utcfromtimestamp(int(time.time()))
|
||||
def_name = str(os.path.splitext(os.path.basename(handle.filename))[0])
|
||||
def_name = str(os.path.splitext(os.path.basename(filename))[0])
|
||||
def_version = int(dt.strftime("%Y%m%d%H%M%S"))
|
||||
mmd.set_name(mmd.get_name() or def_name)
|
||||
mmd.set_stream(stream or mmd.get_stream() or "master")
|
||||
mmd.set_version(mmd.get_version() or def_version)
|
||||
|
||||
|
||||
def submit_module_build_from_yaml(username, handle, stream=None, skiptests=False,
|
||||
optional_params=None):
|
||||
yaml_file = handle.read()
|
||||
mmd = load_mmd(yaml_file)
|
||||
mmd_set_default_nsv(mmd, handle.filename, stream=stream)
|
||||
if skiptests:
|
||||
buildopts = mmd.get_rpm_buildopts()
|
||||
buildopts["macros"] = buildopts.get("macros", "") + "\n\n%__spec_check_pre exit 0\n"
|
||||
@@ -486,6 +551,13 @@ def submit_module_build(username, url, mmd, optional_params=None):
|
||||
modules = []
|
||||
|
||||
for mmd in mmds:
|
||||
# Whatever this expanded modulemd exists, check and record possible
|
||||
# stream collision modules. Corresponding modules could be updated
|
||||
# before an existing module is resubmitted again, so MBS has to ensure
|
||||
# stream collision modules list is up-to-date.
|
||||
log.info('Start to handle potential module stream collision.')
|
||||
record_stream_collision_modules(mmd)
|
||||
|
||||
# Prefix the version of the modulemd based on the base module it buildrequires
|
||||
version = get_prefixed_version(mmd)
|
||||
mmd.set_version(version)
|
||||
|
||||
228
module_build_service/utils/ursine.py
Normal file
228
module_build_service/utils/ursine.py
Normal file
@@ -0,0 +1,228 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Written by Chenxiong Qi <cqi@redhat.com>
|
||||
|
||||
import re
|
||||
|
||||
from module_build_service import conf, log, glib
|
||||
from module_build_service.resolver import system_resolver
|
||||
|
||||
|
||||
"""
|
||||
This module handles module stream collision with ursine content before a module
|
||||
is built.
|
||||
|
||||
This kind of collision would happen when packages, which are managed by
|
||||
Ursa-Major, are used to build a module. Let's see an example, when a module foo
|
||||
buildrequires bar with stream A, however module bar with another stream B was
|
||||
already added to ursine content by Ursa-Major when it has been built and moved
|
||||
to ready state. Hence, MBS has to ensure any packages from module bar:B are not
|
||||
present in the buildroot.
|
||||
|
||||
A technical background:
|
||||
|
||||
Generally, each module buildrequires a platform module, which is associated
|
||||
with ursine content by adding an external repository to the platform module's
|
||||
tag. That repository is generated from a build tag that inherits from a set
|
||||
modules' koji tag through its tag inheritance hierarchy. Ursa-Major manages
|
||||
the inheritance relationship.
|
||||
|
||||
Stream collision modules are just those modules added to tag inheritance by
|
||||
Ursa-Major.
|
||||
"""
|
||||
|
||||
|
||||
def find_build_tags_from_external_repos(koji_session, repo_infos):
|
||||
"""Find build tags from external repos
|
||||
|
||||
An external repo added to a tag could be an arbitrary external repository.
|
||||
Hence, this method tries best to guess the tags from each external repo's
|
||||
URL by a regular expression.
|
||||
|
||||
:param repo_infos: list of mappings represeting external repos information.
|
||||
:type repo_infos: list[dict]
|
||||
:return: a list of tag names.
|
||||
:rtype: list[str]
|
||||
"""
|
||||
re_external_repo_url = r'^{}/repos/(.+-build)/latest/\$arch/?$'.format(
|
||||
koji_session.opts['topurl'].rstrip('/'))
|
||||
tag_names = []
|
||||
for info in repo_infos:
|
||||
om = re.match(re_external_repo_url, info['url'])
|
||||
if om:
|
||||
name = om.groups()[0]
|
||||
if koji_session.getTag(name) is None:
|
||||
log.warning('Ignore found tag %s because no tag info is found '
|
||||
'with this name.', name)
|
||||
else:
|
||||
tag_names.append(name)
|
||||
else:
|
||||
log.warning('The build tag could not be parsed from external repo '
|
||||
'%s whose url is %s.',
|
||||
info['external_repo_name'], info['url'])
|
||||
return tag_names
|
||||
|
||||
|
||||
def find_module_koji_tags(koji_session, build_tag):
|
||||
"""
|
||||
Find module koji tags from parents of build tag through the tag inheritance
|
||||
|
||||
MBS supports a few of prefixes which is configured in
|
||||
``conf.koij_tag_prefixes``. Tags with a configured prefix will be
|
||||
considered as a koji tag.
|
||||
|
||||
:param koji_session: instance of Koji client session.
|
||||
:type koji_session: ClientSession
|
||||
:param str build_tag: tag name, which is the build tag inheriting from
|
||||
parent tags where module koji tags are contained.
|
||||
:return: list of module koji tags.
|
||||
:rtype: list[str]
|
||||
"""
|
||||
return [
|
||||
data['name'] for data in koji_session.getFullInheritance(build_tag)
|
||||
if any(data['name'].startswith(prefix) for prefix in conf.koji_tag_prefixes)
|
||||
]
|
||||
|
||||
|
||||
def get_modulemds_from_ursine_content(tag):
|
||||
"""Get all modules metadata which were added to ursine content
|
||||
|
||||
Ursine content is the tag inheritance managed by Ursa-Major by adding
|
||||
specific modules' koji_tag.
|
||||
|
||||
Background of module build based on ursine content:
|
||||
|
||||
Each module build buildrequires a platform module, which is a presudo-module
|
||||
used to connect to an external repository whose packages could be present
|
||||
in the buildroot. In practice, the external repo is generated from a build
|
||||
tag which could inherit from a few module koji_tags so that those module's
|
||||
RPMs could be build dependencies for some specific packages.
|
||||
|
||||
So, this method is to find out all module koji_tags from the build tag
|
||||
and return corresponding module metadata.
|
||||
|
||||
:param str tag: a base module's koji_tag.
|
||||
:return: list of module metadata. Empty list will be returned if no ursine
|
||||
modules metadata is found.
|
||||
:rtype: list[Modulemd.Module]
|
||||
"""
|
||||
from module_build_service.builder.KojiModuleBuilder import KojiModuleBuilder
|
||||
koji_session = KojiModuleBuilder.get_session(conf, None)
|
||||
repos = koji_session.getExternalRepoList(tag)
|
||||
build_tags = find_build_tags_from_external_repos(koji_session, repos)
|
||||
if not build_tags:
|
||||
log.info('No external repo containing ursine content is found.')
|
||||
return []
|
||||
modulemds = []
|
||||
for tag in build_tags:
|
||||
koji_tags = find_module_koji_tags(koji_session, tag)
|
||||
for koji_tag in koji_tags:
|
||||
md = system_resolver.get_modulemd_by_koji_tag(koji_tag)
|
||||
if md:
|
||||
modulemds.append(md)
|
||||
else:
|
||||
log.warning('No module is found by koji_tag \'%s\'', koji_tag)
|
||||
return modulemds
|
||||
|
||||
|
||||
def find_stream_collision_modules(buildrequired_modules, koji_tag):
|
||||
"""
|
||||
Find out modules from ursine content which are buildrequires but with a
|
||||
different stream.
|
||||
|
||||
:param dict buildrequired_modules: a mapping of buildrequires, which is just
|
||||
the ``xmd/mbs/buildrequires``. This mapping is used to determine if a module
|
||||
found from ursine content is a buildrequire with different stream.
|
||||
:param str koji_tag: a base module's koji_tag. Modules will be retrieve from
|
||||
ursince content associated with this koji_tag and check if there are
|
||||
collision modules.
|
||||
:return: a list of NSVC of collision modules. If no collision module is
|
||||
found, false value is returned.
|
||||
:rtype: list[str]
|
||||
"""
|
||||
ursine_modulemds = get_modulemds_from_ursine_content(koji_tag)
|
||||
if not ursine_modulemds:
|
||||
log.info('No module metadata is found from ursine content.')
|
||||
return
|
||||
|
||||
collision_modules = [
|
||||
item.dup_nsvc()
|
||||
for item in ursine_modulemds
|
||||
# If some module in the ursine content is one of the buildrequires but has
|
||||
# different stream, that is what we want to record here, whose RPMs will be
|
||||
# excluded from buildroot by adding them into SRPM module-build-macros as
|
||||
# Conflicts.
|
||||
if (item.get_name() in buildrequired_modules and
|
||||
item.get_stream() != buildrequired_modules[item.get_name()]['stream'])
|
||||
]
|
||||
|
||||
for item in collision_modules:
|
||||
name, stream, _ = item.split(':', 2)
|
||||
log.info('Buildrequired module %s exists in ursine content with '
|
||||
'different stream %s, whose RPMs will be excluded.',
|
||||
name, stream)
|
||||
|
||||
return collision_modules
|
||||
|
||||
|
||||
def record_stream_collision_modules(mmd):
|
||||
"""
|
||||
Find out modules from ursine content and record those that are buildrequire
|
||||
module but have different stream.
|
||||
|
||||
Note that, this handle depends on the result of module stream expansion.
|
||||
|
||||
MBS supports multiple base modules via option conf.base_module_names. A base
|
||||
module name could be platform in most cases, but there could be others for
|
||||
particular cases in practice. So, each expanded base module stored in
|
||||
``xmd/mbs/buildrequires`` will be handled and will have a new
|
||||
key/value pair ``stream_collision_modules: [N-S-V-C, ...]``. This key/value
|
||||
will be handled in module event handler then.
|
||||
|
||||
As a result, a new item is added xmd/mbs/buildrequires/platform/stream_collision_modules,
|
||||
which is a list of NSVC strings. Each of them is the module added to ursine
|
||||
content by Ursa-Major.
|
||||
|
||||
:param mmd: a module's metadata which will be built.
|
||||
:type mmd: Modulemd.Module
|
||||
"""
|
||||
unpacked_xmd = glib.from_variant_dict(mmd.get_xmd())
|
||||
buildrequires = unpacked_xmd['mbs']['buildrequires']
|
||||
|
||||
for module_name in conf.base_module_names:
|
||||
base_module_info = buildrequires.get(module_name)
|
||||
if base_module_info is None:
|
||||
log.warning(
|
||||
'Base module %s is not a buildrequire of module %s. '
|
||||
'Skip handling module stream collision for this base module.',
|
||||
mmd.get_name())
|
||||
continue
|
||||
|
||||
modules_nsvc = find_stream_collision_modules(
|
||||
buildrequires, base_module_info['koji_tag'])
|
||||
if modules_nsvc:
|
||||
base_module_info['stream_collision_modules'] = modules_nsvc
|
||||
else:
|
||||
log.info('No stream collision module is found against base module %s.',
|
||||
module_name)
|
||||
|
||||
mmd.set_xmd(glib.dict_values(unpacked_xmd))
|
||||
@@ -693,18 +693,27 @@ def reuse_shared_userspace_init_data():
|
||||
session.add(build)
|
||||
|
||||
|
||||
def make_module(nsvc, requires_list, build_requires_list, base_module=None):
|
||||
def make_module(nsvc, requires_list=None, build_requires_list=None, base_module=None,
|
||||
filtered_rpms=None, xmd=None, store_to_db=True):
|
||||
"""
|
||||
Creates new models.ModuleBuild defined by `nsvc` string with requires
|
||||
and buildrequires set according to `requires_list` and `build_requires_list`.
|
||||
and buildrequires set according to ``requires_list`` and ``build_requires_list``.
|
||||
|
||||
:param str nsvc: name:stream:version:context of a module.
|
||||
:param list_of_dicts requires_list: List of dictionaries defining the
|
||||
requires in the mmd requires field format.
|
||||
:param list_of_dicts build_requires_list: List of dictionaries defining the
|
||||
build_requires_list in the mmd build_requires_list field format.
|
||||
:rtype: ModuleBuild
|
||||
:return: New Module Build.
|
||||
:param filtered_rpms: list of filtered RPMs which are added to filter
|
||||
section in module metadata.
|
||||
:type filtered_rpms: list[str]
|
||||
:param dict xmd: a mapping representing XMD section in module metadata. A
|
||||
custom xmd could be passed for particular test purpose and some default
|
||||
key/value pairs are added if not present.
|
||||
:param bool store_to_db: whether to store create module metadata to database.
|
||||
:return: New Module Build if set to store module metadata to database,
|
||||
otherwise the module metadata is returned.
|
||||
:rtype: ModuleBuild or Modulemd.Module
|
||||
"""
|
||||
name, stream, version, context = nsvc.split(":")
|
||||
mmd = Modulemd.Module()
|
||||
@@ -719,30 +728,46 @@ def make_module(nsvc, requires_list, build_requires_list, base_module=None):
|
||||
licenses.add("GPL")
|
||||
mmd.set_module_licenses(licenses)
|
||||
|
||||
if not isinstance(requires_list, list):
|
||||
requires_list = [requires_list]
|
||||
if not isinstance(build_requires_list, list):
|
||||
build_requires_list = [build_requires_list]
|
||||
if filtered_rpms:
|
||||
rpm_filter_set = Modulemd.SimpleSet()
|
||||
rpm_filter_set.set(filtered_rpms)
|
||||
mmd.set_rpm_filter(rpm_filter_set)
|
||||
|
||||
if requires_list is not None and build_requires_list is not None:
|
||||
if not isinstance(requires_list, list):
|
||||
requires_list = [requires_list]
|
||||
if not isinstance(build_requires_list, list):
|
||||
build_requires_list = [build_requires_list]
|
||||
|
||||
deps_list = []
|
||||
for requires, build_requires in zip(requires_list,
|
||||
build_requires_list):
|
||||
deps = Modulemd.Dependencies()
|
||||
for req_name, req_streams in requires.items():
|
||||
deps.add_requires(req_name, req_streams)
|
||||
for req_name, req_streams in build_requires.items():
|
||||
deps.add_buildrequires(req_name, req_streams)
|
||||
deps_list.append(deps)
|
||||
mmd.set_dependencies(deps_list)
|
||||
|
||||
# Caller could pass whole xmd including mbs, but if something is missing,
|
||||
# default values are given here.
|
||||
xmd = xmd or {'mbs': {}}
|
||||
xmd_mbs = xmd['mbs']
|
||||
if 'buildrequires' not in xmd_mbs:
|
||||
xmd_mbs['buildrequires'] = {}
|
||||
if 'requires' not in xmd_mbs:
|
||||
xmd_mbs['requires'] = {}
|
||||
if 'commit' not in xmd_mbs:
|
||||
xmd_mbs['commit'] = 'ref_%s' % context
|
||||
if 'mse' not in xmd_mbs:
|
||||
xmd_mbs['mse'] = 'true'
|
||||
|
||||
xmd = {
|
||||
"mbs": {
|
||||
"buildrequires": {},
|
||||
"requires": {},
|
||||
"commit": "ref_%s" % context,
|
||||
"mse": "true",
|
||||
}
|
||||
}
|
||||
deps_list = []
|
||||
for requires, build_requires in zip(requires_list, build_requires_list):
|
||||
deps = Modulemd.Dependencies()
|
||||
for req_name, req_streams in requires.items():
|
||||
deps.add_requires(req_name, req_streams)
|
||||
for req_name, req_streams in build_requires.items():
|
||||
deps.add_buildrequires(req_name, req_streams)
|
||||
deps_list.append(deps)
|
||||
mmd.set_dependencies(deps_list)
|
||||
mmd.set_xmd(glib.dict_values(xmd))
|
||||
|
||||
if not store_to_db:
|
||||
return mmd
|
||||
|
||||
module_build = ModuleBuild()
|
||||
module_build.name = name
|
||||
module_build.stream = stream
|
||||
@@ -762,6 +787,8 @@ def make_module(nsvc, requires_list, build_requires_list, base_module=None):
|
||||
module_build.modulemd = mmd.dumps()
|
||||
if base_module:
|
||||
module_build.buildrequires.append(base_module)
|
||||
if 'koji_tag' in xmd['mbs']:
|
||||
module_build.koji_tag = xmd['mbs']['koji_tag']
|
||||
db.session.add(module_build)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@@ -292,6 +292,7 @@ def cleanup_moksha():
|
||||
import moksha.hub.reactor # noqa
|
||||
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch.object(module_build_service.config.Config, 'system', new_callable=PropertyMock,
|
||||
return_value='test')
|
||||
@patch("module_build_service.builder.GenericBuilder.default_buildroot_groups",
|
||||
@@ -326,7 +327,7 @@ class TestBuild:
|
||||
@pytest.mark.parametrize('mmd_version', [1, 2])
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build(self, mocked_scm, mocked_get_user, conf_system, dbg,
|
||||
def test_submit_build(self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc,
|
||||
mmd_version):
|
||||
"""
|
||||
Tests the build of testmodule.yaml using FakeModuleBuilder which
|
||||
@@ -392,7 +393,7 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_no_components(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_no_components(self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests the build of a module with no components
|
||||
"""
|
||||
@@ -420,7 +421,7 @@ class TestBuild:
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_eol_module(self, mocked_scm, mocked_get_user, is_eol, check,
|
||||
conf_system, dbg):
|
||||
conf_system, dbg, hmsc):
|
||||
""" Tests the build of a module with an eol stream. """
|
||||
FakeSCM(mocked_scm, 'python3', 'python3-no-components.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
@@ -436,7 +437,7 @@ class TestBuild:
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_from_yaml_not_allowed(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
FakeSCM(mocked_scm, "testmodule", "testmodule.yaml")
|
||||
|
||||
testmodule = os.path.join(base_dir, 'staged_data', 'testmodule.yaml')
|
||||
@@ -454,7 +455,8 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_from_yaml_allowed(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_from_yaml_allowed(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
testmodule = os.path.join(base_dir, 'staged_data', 'testmodule.yaml')
|
||||
@@ -475,7 +477,7 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_cancel(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_cancel(self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Submit all builds for a module and cancel the module build later.
|
||||
"""
|
||||
@@ -526,7 +528,8 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_instant_complete(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_instant_complete(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests the build of testmodule.yaml using FakeModuleBuilder which
|
||||
succeeds everytime.
|
||||
@@ -559,7 +562,7 @@ class TestBuild:
|
||||
new_callable=PropertyMock, return_value=1)
|
||||
def test_submit_build_concurrent_threshold(self, conf_num_concurrent_builds,
|
||||
mocked_scm, mocked_get_user,
|
||||
conf_system, dbg):
|
||||
conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests the build of testmodule.yaml using FakeModuleBuilder with
|
||||
num_concurrent_builds set to 1.
|
||||
@@ -603,7 +606,7 @@ class TestBuild:
|
||||
new_callable=PropertyMock, return_value=2)
|
||||
def test_try_to_reach_concurrent_threshold(self, conf_num_concurrent_builds,
|
||||
mocked_scm, mocked_get_user,
|
||||
conf_system, dbg):
|
||||
conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that we try to submit new component build right after
|
||||
the previous one finished without waiting for all
|
||||
@@ -652,7 +655,7 @@ class TestBuild:
|
||||
@patch("module_build_service.config.Config.num_concurrent_builds",
|
||||
new_callable=PropertyMock, return_value=1)
|
||||
def test_build_in_batch_fails(self, conf_num_concurrent_builds, mocked_scm,
|
||||
mocked_get_user, conf_system, dbg):
|
||||
mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that if the build in batch fails, other components in a batch
|
||||
are still build, but next batch is not started.
|
||||
@@ -711,7 +714,7 @@ class TestBuild:
|
||||
@patch("module_build_service.config.Config.num_concurrent_builds",
|
||||
new_callable=PropertyMock, return_value=1)
|
||||
def test_all_builds_in_batch_fail(self, conf_num_concurrent_builds, mocked_scm,
|
||||
mocked_get_user, conf_system, dbg):
|
||||
mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that if the build in batch fails, other components in a batch
|
||||
are still build, but next batch is not started.
|
||||
@@ -755,7 +758,7 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_reuse_all(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_reuse_all(self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that we do not try building module-build-macros when reusing all
|
||||
components in a module build.
|
||||
@@ -806,7 +809,7 @@ class TestBuild:
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_reuse_all_without_build_macros(self, mocked_scm, mocked_get_user,
|
||||
conf_system, dbg):
|
||||
conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that we can reuse components even when the reused module does
|
||||
not have module-build-macros component.
|
||||
@@ -858,7 +861,7 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_resume(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_resume(self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that resuming the build works even when previous batches
|
||||
are already built.
|
||||
@@ -982,7 +985,7 @@ class TestBuild:
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_resume_recover_orphaned_macros(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that resuming the build works when module-build-macros is orphaned but marked as
|
||||
failed in the database
|
||||
@@ -1097,7 +1100,8 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_resume_failed_init(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_resume_failed_init(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that resuming the build works when the build failed during the init step
|
||||
"""
|
||||
@@ -1151,7 +1155,8 @@ class TestBuild:
|
||||
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_resume_init_fail(self, mocked_scm, mocked_get_user, conf_system, dbg):
|
||||
def test_submit_build_resume_init_fail(
|
||||
self, mocked_scm, mocked_get_user, conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that resuming the build fails when the build is in init state
|
||||
"""
|
||||
@@ -1181,7 +1186,7 @@ class TestBuild:
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_repo_regen_not_started_batch(self, mocked_scm, mocked_get_user,
|
||||
conf_system, dbg):
|
||||
conf_system, dbg, hmsc):
|
||||
"""
|
||||
Tests that if MBS starts a new batch, the concurrent component threshold is met before a
|
||||
build can start, and an unexpected repo regen occurs, the build will not fail.
|
||||
@@ -1254,13 +1259,14 @@ class TestLocalBuild:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch("module_build_service.config.Config.mock_resultsdir",
|
||||
new_callable=PropertyMock,
|
||||
return_value=path.join(base_dir, 'staged_data', "local_builds"))
|
||||
def test_submit_build_local_dependency(
|
||||
self, resultsdir, mocked_scm, mocked_get_user, conf_system):
|
||||
self, resultsdir, mocked_scm, mocked_get_user, conf_system, hmsc):
|
||||
"""
|
||||
Tests local module build dependency.
|
||||
"""
|
||||
|
||||
@@ -19,9 +19,13 @@
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Written by Jan Kaluza <jkaluza@redhat.com>
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
import koji
|
||||
|
||||
try:
|
||||
import xmlrpclib
|
||||
except ImportError:
|
||||
@@ -37,7 +41,7 @@ from module_build_service import glib, db
|
||||
import pytest
|
||||
from mock import patch, MagicMock
|
||||
|
||||
from tests import conf, init_data, reuse_component_init_data
|
||||
from tests import conf, init_data, reuse_component_init_data, clean_database
|
||||
|
||||
from module_build_service.builder.KojiModuleBuilder import KojiModuleBuilder
|
||||
|
||||
@@ -634,3 +638,82 @@ class TestKojiBuilder:
|
||||
current_module = module_build_service.models.ModuleBuild.query.get(3)
|
||||
rv = KojiModuleBuilder._get_filtered_rpms_on_self_dep(current_module, br_filtered_rpms)
|
||||
assert set(rv) == set(expected)
|
||||
|
||||
|
||||
class TestGetDistTagSRPM:
|
||||
"""Test KojiModuleBuilder.get_disttag_srpm"""
|
||||
|
||||
def setup_method(self):
|
||||
clean_database()
|
||||
|
||||
self.tmp_srpm_build_dir = tempfile.mkdtemp(prefix='test-koji-builder-')
|
||||
self.spec_file = os.path.join(self.tmp_srpm_build_dir, 'module-build-macros.spec')
|
||||
self.srpms_dir = os.path.join(self.tmp_srpm_build_dir, 'SRPMS')
|
||||
os.mkdir(self.srpms_dir)
|
||||
self.expected_srpm_file = os.path.join(
|
||||
self.srpms_dir, 'module-build-macros.src.rpm')
|
||||
|
||||
# Don't care about the content, just assert the existence.
|
||||
with open(self.expected_srpm_file, 'w') as f:
|
||||
f.write('')
|
||||
|
||||
module_nsvc = dict(
|
||||
name='testmodule',
|
||||
stream='master',
|
||||
version='1',
|
||||
context=module_build_service.models.DEFAULT_MODULE_CONTEXT
|
||||
)
|
||||
|
||||
xmd = {
|
||||
'mbs': {
|
||||
'buildrequires': {
|
||||
'modulea': {
|
||||
'filtered_rpms': ['baz-devel-0:0.1-6.fc28',
|
||||
'baz-doc-0:0.1-6.fc28'],
|
||||
},
|
||||
'platform': {
|
||||
'filtered_rpms': [],
|
||||
'stream_collision_modules': ['modulefoo-s-v-c'],
|
||||
'ursine_rpms': ['foo-0:1.0-1.fc28', 'bar-0:2.0-1.fc28']
|
||||
}
|
||||
},
|
||||
'koji_tag': 'module-{name}-{stream}-{version}-{context}'
|
||||
.format(**module_nsvc)
|
||||
}
|
||||
}
|
||||
from tests import make_module
|
||||
self.module_build = make_module(
|
||||
'{name}:{stream}:{version}:{context}'.format(**module_nsvc),
|
||||
xmd=xmd)
|
||||
|
||||
def teardown_method(self):
|
||||
shutil.rmtree(self.tmp_srpm_build_dir)
|
||||
clean_database()
|
||||
|
||||
@patch('tempfile.mkdtemp')
|
||||
@patch('module_build_service.builder.KojiModuleBuilder.execute_cmd')
|
||||
def _build_srpm(self, execute_cmd, mkdtemp):
|
||||
mkdtemp.return_value = self.tmp_srpm_build_dir
|
||||
return KojiModuleBuilder.get_disttag_srpm('disttag', self.module_build)
|
||||
|
||||
def test_return_srpm_file(self):
|
||||
srpm_file = self._build_srpm()
|
||||
assert self.expected_srpm_file == srpm_file
|
||||
|
||||
def test_filtered_rpms_are_added(self):
|
||||
self._build_srpm()
|
||||
|
||||
with open(self.spec_file, 'r') as f:
|
||||
content = f.read()
|
||||
for nevr in ['baz-devel-0:0.1-6.fc28', 'baz-doc-0:0.1-6.fc28']:
|
||||
assert KojiModuleBuilder.format_conflicts_line(nevr) + '\n' in content
|
||||
|
||||
def test_ursine_rpms_are_added(self):
|
||||
self._build_srpm()
|
||||
|
||||
with open(self.spec_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
assert '# modulefoo-s-v-c\n' in content
|
||||
for nevr in ['foo-0:1.0-1.fc28', 'bar-0:2.0-1.fc28']:
|
||||
assert KojiModuleBuilder.format_conflicts_line(nevr) + '\n' in content
|
||||
|
||||
322
tests/test_utils/test_ursine.py
Normal file
322
tests/test_utils/test_ursine.py
Normal file
@@ -0,0 +1,322 @@
|
||||
# Copyright (c) 2017 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from mock import patch, Mock
|
||||
|
||||
from module_build_service import conf, glib
|
||||
from module_build_service.utils import ursine
|
||||
from tests import make_module, clean_database
|
||||
|
||||
|
||||
class TestFindModuleKojiTags:
|
||||
"""Test ursine.find_module_koji_tags"""
|
||||
|
||||
@patch.object(conf, 'koji_tag_prefixes', new=['module'])
|
||||
def test_find_out_all_module_koji_tags(self):
|
||||
session = Mock()
|
||||
session.getFullInheritance.return_value = [
|
||||
{'name': 'module-tag1-s-v-c'},
|
||||
{'name': 'module-tag2-s-v-c'},
|
||||
{'name': 'tag-1'},
|
||||
]
|
||||
|
||||
expected_tags = ['module-tag1-s-v-c', 'module-tag2-s-v-c']
|
||||
|
||||
tags = ursine.find_module_koji_tags(session, 'tag-a-build')
|
||||
assert expected_tags == tags
|
||||
|
||||
@patch.object(conf, 'koji_tag_prefixes', new=['module'])
|
||||
def test_return_empty_if_no_module_koji_tags(self):
|
||||
session = Mock()
|
||||
session.getFullInheritance.return_value = [
|
||||
{'name': 'tag-1'}, {'name': 'tag-2'},
|
||||
]
|
||||
|
||||
tags = ursine.find_module_koji_tags(session, 'tag-a-build')
|
||||
assert [] == tags
|
||||
|
||||
|
||||
class TestFindUrsineRootTags:
|
||||
"""Test ursine.find_build_tags_from_external_repos"""
|
||||
|
||||
def setup_method(self):
|
||||
self.koji_session = Mock()
|
||||
self.koji_session.opts = {'topurl': 'http://example.com/brewroot/'}
|
||||
self.koji_session.getTag.side_effect = lambda name: \
|
||||
None if name == 'X-build' else {'name': name}
|
||||
|
||||
def test_find_build_tags(self):
|
||||
tags = ursine.find_build_tags_from_external_repos(self.koji_session, [
|
||||
{
|
||||
'external_repo_name': 'tag-1-external-repo',
|
||||
'url': 'http://example.com/brewroot/repos/tag-1-build/latest/$arch/'
|
||||
},
|
||||
{
|
||||
'external_repo_name': 'tag-2-external-repo',
|
||||
'url': 'http://example.com/brewroot/repos/tag-2-build/latest/$arch/'
|
||||
},
|
||||
])
|
||||
|
||||
assert ['tag-1-build', 'tag-2-build'] == tags
|
||||
|
||||
def test_return_emtpy_if_no_match_external_repo_url(self):
|
||||
tags = ursine.find_build_tags_from_external_repos(self.koji_session, [
|
||||
{
|
||||
'external_repo_name': 'tag-1-external-repo',
|
||||
'url': 'https://another-site.org/repos/tag-1-build/latest/$arch/'
|
||||
},
|
||||
{
|
||||
'external_repo_name': 'tag-2-external-repo',
|
||||
'url': 'https://another-site.org/repos/tag-2-build/latest/$arch/'
|
||||
},
|
||||
])
|
||||
|
||||
assert [] == tags
|
||||
|
||||
def test_some_tag_is_not_koji_tag(self):
|
||||
tags = ursine.find_build_tags_from_external_repos(self.koji_session, [
|
||||
{
|
||||
'external_repo_name': 'tag-1-external-repo',
|
||||
'url': 'http://example.com/brewroot/repos/tag-1-build/latest/$arch/'
|
||||
},
|
||||
{
|
||||
'external_repo_name': 'tag-2-external-repo',
|
||||
'url': 'http://example.com/brewroot/repos/X-build/latest/$arch/'
|
||||
},
|
||||
])
|
||||
|
||||
assert ['tag-1-build'] == tags
|
||||
|
||||
|
||||
class TestGetModulemdsFromUrsineContent:
|
||||
"""Test ursine.get_modulemds_from_ursine_content"""
|
||||
|
||||
def setup_method(self):
|
||||
clean_database(False)
|
||||
self.koji_session = Mock()
|
||||
self.koji_session.opts = {'topurl': 'https://example.com/'}
|
||||
|
||||
def teardown_method(self, test_method):
|
||||
clean_database()
|
||||
|
||||
@patch('koji.ClientSession')
|
||||
def test_return_empty_if_no_ursine_build_tag_is_found(self, ClientSession):
|
||||
session = ClientSession.return_value
|
||||
session.opts = {'topurl': 'http://example.com/'}
|
||||
|
||||
# No module koji_tag in ursine content yet. This will result in empty
|
||||
# ursine modulemds is returned.
|
||||
session.getFullInheritance.return_value = [
|
||||
{'name': 'tag-1.0-build'},
|
||||
]
|
||||
session.getExternalRepoList.return_value = [
|
||||
{
|
||||
'external_repo_name': 'tag-1.0-external-repo',
|
||||
'url': 'http://example.com/repos/tag-4-build/latest/$arch/'
|
||||
}
|
||||
]
|
||||
|
||||
modulemds = ursine.get_modulemds_from_ursine_content('tag')
|
||||
assert [] == modulemds
|
||||
|
||||
@patch.object(conf, 'koji_tag_prefixes', new=['module'])
|
||||
@patch('koji.ClientSession')
|
||||
def test_get_modulemds(self, ClientSession):
|
||||
session = ClientSession.return_value
|
||||
session.opts = {'topurl': 'http://example.com/'}
|
||||
|
||||
# Ensure to to get build tag for further query of ursine content.
|
||||
# For this test, the build tag is tag-4-build
|
||||
session.getExternalRepoList.return_value = [
|
||||
{
|
||||
'external_repo_name': 'tag-1.0-external-repo',
|
||||
'url': 'http://example.com/repos/tag-4-build/latest/$arch/'
|
||||
}
|
||||
]
|
||||
|
||||
# Ensure to return module tags from ursine content of fake build tag
|
||||
# specified in above external repo's url.
|
||||
def mock_getFullInheritance(tag):
|
||||
if tag == 'tag-4-build':
|
||||
return [
|
||||
{'name': 'tag-1.0-build'},
|
||||
# Below two modules should be returned and whose modulemd
|
||||
# should be also queried from database.
|
||||
{'name': 'module-name1-s-2020-c'},
|
||||
{'name': 'module-name2-s-2021-c'},
|
||||
]
|
||||
raise ValueError('{} is not handled by test.'.format(tag))
|
||||
session.getFullInheritance.side_effect = mock_getFullInheritance
|
||||
|
||||
# Defaults to DB resolver, so create fake module builds and store them
|
||||
# into database to ensure they can be queried.
|
||||
mmd_name1s2020c = make_module(
|
||||
'name1:s:2020:c',
|
||||
xmd={'mbs': {'koji_tag': 'module-name1-s-2020-c'}})
|
||||
mmd_name2s2021c = make_module(
|
||||
'name2:s:2021:c',
|
||||
xmd={'mbs': {'koji_tag': 'module-name2-s-2021-c'}})
|
||||
|
||||
koji_tag = 'tag' # It's ok to use arbitrary tag name.
|
||||
modulemds = ursine.get_modulemds_from_ursine_content(koji_tag)
|
||||
|
||||
test_nsvcs = [item.dup_nsvc() for item in modulemds]
|
||||
test_nsvcs.sort()
|
||||
|
||||
expected_nsvcs = [mmd_name1s2020c.mmd().dup_nsvc(),
|
||||
mmd_name2s2021c.mmd().dup_nsvc()]
|
||||
expected_nsvcs.sort()
|
||||
|
||||
session.getExternalRepoList.assert_called_once_with(koji_tag)
|
||||
assert expected_nsvcs == test_nsvcs
|
||||
|
||||
|
||||
class TestRecordStreamCollisionModules:
|
||||
"""Test ursine.record_stream_collision_modules"""
|
||||
|
||||
@patch.object(conf, 'base_module_names', new=['platform'])
|
||||
@patch.object(ursine, 'find_stream_collision_modules')
|
||||
def test_nothing_changed_if_no_base_module_is_in_buildrequires(
|
||||
self, find_stream_collision_modules):
|
||||
xmd = {
|
||||
'mbs': {
|
||||
'buildrequires': {
|
||||
'modulea': {'stream': 'master'}
|
||||
}
|
||||
}
|
||||
}
|
||||
fake_mmd = make_module('name1:s:2020:c', xmd=xmd, store_to_db=False)
|
||||
original_xmd = glib.from_variant_dict(fake_mmd.get_xmd())
|
||||
|
||||
with patch.object(ursine, 'log') as log:
|
||||
ursine.record_stream_collision_modules(fake_mmd)
|
||||
log.warning.assert_called_once()
|
||||
find_stream_collision_modules.assert_not_called()
|
||||
|
||||
assert original_xmd == glib.from_variant_dict(fake_mmd.get_xmd())
|
||||
|
||||
@patch.object(conf, 'base_module_names', new=['platform'])
|
||||
@patch('module_build_service.utils.ursine.get_modulemds_from_ursine_content')
|
||||
def test_nothing_changed_if_no_modules_in_ursine_content(
|
||||
self, get_modulemds_from_ursine_content):
|
||||
xmd = {
|
||||
'mbs': {
|
||||
'buildrequires': {
|
||||
'modulea': {'stream': 'master'},
|
||||
'platform': {'stream': 'master', 'koji_tag': 'module-rhel-8.0-build'},
|
||||
}
|
||||
}
|
||||
}
|
||||
fake_mmd = make_module('name1:s:2020:c', xmd=xmd, store_to_db=False)
|
||||
original_xmd = glib.from_variant_dict(fake_mmd.get_xmd())
|
||||
|
||||
get_modulemds_from_ursine_content.return_value = []
|
||||
|
||||
with patch.object(ursine, 'log') as log:
|
||||
ursine.record_stream_collision_modules(fake_mmd)
|
||||
log.info.assert_called()
|
||||
|
||||
assert original_xmd == glib.from_variant_dict(fake_mmd.get_xmd())
|
||||
|
||||
@patch.object(conf, 'base_module_names', new=['platform', 'project-platform'])
|
||||
@patch('module_build_service.utils.ursine.get_modulemds_from_ursine_content')
|
||||
def test_add_collision_modules(self, get_modulemds_from_ursine_content):
|
||||
xmd = {
|
||||
'mbs': {
|
||||
'buildrequires': {
|
||||
'modulea': {'stream': 'master'},
|
||||
'foo': {'stream': '1'},
|
||||
'bar': {'stream': '2'},
|
||||
'platform': {'stream': 'master', 'koji_tag': 'module-rhel-8.0-build'},
|
||||
'project-platform': {
|
||||
'stream': 'master', 'koji_tag': 'module-project-1.0-build'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
fake_mmd = make_module('name1:s:2020:c', xmd=xmd, store_to_db=False)
|
||||
|
||||
def mock_get_ursine_modulemds(koji_tag):
|
||||
if koji_tag == 'module-rhel-8.0-build':
|
||||
return [
|
||||
make_module('modulea:10:20180813041838:5ea3b708', store_to_db=False),
|
||||
make_module('moduleb:1.0:20180113042038:6ea3b105', store_to_db=False),
|
||||
]
|
||||
if koji_tag == 'module-project-1.0-build':
|
||||
return [
|
||||
make_module('bar:6:20181013041838:817fa3a8', store_to_db=False),
|
||||
make_module('foo:2:20180113041838:95f078a1', store_to_db=False),
|
||||
]
|
||||
|
||||
get_modulemds_from_ursine_content.side_effect = mock_get_ursine_modulemds
|
||||
|
||||
ursine.record_stream_collision_modules(fake_mmd)
|
||||
|
||||
xmd = glib.from_variant_dict(fake_mmd.get_xmd())
|
||||
buildrequires = xmd['mbs']['buildrequires']
|
||||
|
||||
assert (['modulea:10:20180813041838:5ea3b708'] ==
|
||||
buildrequires['platform']['stream_collision_modules'])
|
||||
|
||||
modules = buildrequires['project-platform']['stream_collision_modules']
|
||||
modules.sort()
|
||||
expected_modules = ['bar:6:20181013041838:817fa3a8', 'foo:2:20180113041838:95f078a1']
|
||||
assert expected_modules == modules
|
||||
|
||||
|
||||
class TestFindStreamCollisionModules:
|
||||
"""Test ursine.find_stream_collision_modules"""
|
||||
|
||||
@patch('module_build_service.utils.ursine.get_modulemds_from_ursine_content')
|
||||
def test_no_modulemds_found_from_ursine_content(
|
||||
self, get_modulemds_from_ursine_content):
|
||||
get_modulemds_from_ursine_content.return_value = []
|
||||
assert not ursine.find_stream_collision_modules({}, 'koji_tag')
|
||||
|
||||
@patch('module_build_service.utils.ursine.get_modulemds_from_ursine_content')
|
||||
def test_no_collisions_found(self, get_modulemds_from_ursine_content):
|
||||
xmd_mbs_buildrequires = {
|
||||
'modulea': {'stream': 'master'},
|
||||
'moduleb': {'stream': '10'},
|
||||
}
|
||||
get_modulemds_from_ursine_content.return_value = [
|
||||
make_module('moduler:1:1:c1', store_to_db=False),
|
||||
make_module('modules:2:1:c2', store_to_db=False),
|
||||
make_module('modulet:3:1:c3', store_to_db=False),
|
||||
]
|
||||
assert [] == ursine.find_stream_collision_modules(
|
||||
xmd_mbs_buildrequires, 'koji_tag')
|
||||
|
||||
@patch('module_build_service.utils.ursine.get_modulemds_from_ursine_content')
|
||||
def test_collision_modules_are_found(self, get_modulemds_from_ursine_content):
|
||||
xmd_mbs_buildrequires = {
|
||||
'modulea': {'stream': 'master'},
|
||||
'moduleb': {'stream': '10'},
|
||||
}
|
||||
fake_modules = [
|
||||
make_module('moduler:1:1:c1', store_to_db=False),
|
||||
make_module('moduleb:6:1:c2', store_to_db=False),
|
||||
make_module('modulet:3:1:c3', store_to_db=False),
|
||||
]
|
||||
get_modulemds_from_ursine_content.return_value = fake_modules
|
||||
|
||||
assert [fake_modules[1].dup_nsvc()] == \
|
||||
ursine.find_stream_collision_modules(
|
||||
xmd_mbs_buildrequires, 'koji_tag')
|
||||
@@ -38,7 +38,7 @@ import module_build_service.scheduler.handlers.components
|
||||
from module_build_service.builder.base import GenericBuilder
|
||||
from module_build_service.builder.KojiModuleBuilder import KojiModuleBuilder
|
||||
from module_build_service import glib, Modulemd
|
||||
from tests import app
|
||||
from tests import app, make_module
|
||||
|
||||
BASE_DIR = path.abspath(path.dirname(__file__))
|
||||
|
||||
@@ -1093,3 +1093,124 @@ class TestLocalBuilds:
|
||||
assert len(local_modules) == 1
|
||||
assert local_modules[0].koji_tag.endswith(
|
||||
"/module-platform-f28-3/results")
|
||||
|
||||
|
||||
class TestRecordFilteredRPMs:
|
||||
"""Test submit.record_filtered_rpms"""
|
||||
|
||||
def setup_method(self):
|
||||
clean_database(add_platform_module=False)
|
||||
|
||||
# Prepare fake data for resolver.get_module_modulemds
|
||||
xmd = {
|
||||
'mbs': {
|
||||
'buildrequires': {
|
||||
# This module has its own filtered_rpms, which should be unchanged.
|
||||
'modulea': {'stream': '201808', 'version': '2', 'context': '00000000',
|
||||
'filtered_rpms': ['pkg-1.0-1.fc28']},
|
||||
|
||||
# Whereas no filtered_rpms in this module, filtered rpms should be
|
||||
# injected eventually.
|
||||
'moduleb': {'stream': '7', 'version': '2', 'context': '00000000'},
|
||||
|
||||
# For test recording rpms included in stream collision modules
|
||||
'platform': {'stream': 'f28', 'version': '2', 'context': '00000000',
|
||||
# Don't care about platform module's filtered rpms here
|
||||
'filtered_rpms': [],
|
||||
'stream_collision_modules': ['foo:master:1:00000000',
|
||||
'bar:master:2:00000000']},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.mmd = make_module('cool-module:201809:1:00000000', xmd=xmd, store_to_db=False)
|
||||
|
||||
# Store buildrequires modules into database in order to be queried in the test
|
||||
for module_name, md_dict in xmd['mbs']['buildrequires'].items():
|
||||
br_module_nsvc = (module_name, md_dict['stream'],
|
||||
md_dict['version'], md_dict['context'])
|
||||
br_module_xmd = {
|
||||
'mbs': {
|
||||
'koji_tag': 'module-{}-{}-{}-{}'.format(*br_module_nsvc),
|
||||
}
|
||||
}
|
||||
br_module_filtered_rpms = None
|
||||
if module_name == 'moduleb':
|
||||
# Add this RPM to filter section.
|
||||
# Test will verify if this package is listed in final filtered_rpms list.
|
||||
br_module_filtered_rpms = ['foo']
|
||||
|
||||
# Modules have to be created in database whose NSVC appears in
|
||||
# stream_collision_modules. This is for testing recording rpms
|
||||
# built for stream collision modules.
|
||||
if module_name == 'platform':
|
||||
for nsvc in md_dict['stream_collision_modules']:
|
||||
make_module(nsvc, xmd={'mbs': {
|
||||
'koji_tag': 'module-{}-{}-{}-{}'.format(*nsvc.split(':'))
|
||||
}})
|
||||
|
||||
make_module('{}:{}:{}:{}'.format(*br_module_nsvc),
|
||||
filtered_rpms=br_module_filtered_rpms,
|
||||
xmd=br_module_xmd)
|
||||
|
||||
def teardown_method(self):
|
||||
clean_database()
|
||||
|
||||
# For simplicity, test just queries database. So, no need to code more for
|
||||
# mocking remote MBS service.
|
||||
@patch.object(module_build_service.conf, 'resolver', new='db')
|
||||
@patch('module_build_service.builder.KojiModuleBuilder.KojiModuleBuilder.get_session')
|
||||
def test_generate_and_store_filtered_rpms(self, get_session):
|
||||
|
||||
def mocklistTaggedRPMs(tag, latest):
|
||||
# Result returned from listTaggedRPMs should contain two lists.
|
||||
# The second one is the build, which is not used in test and
|
||||
# omitted here.
|
||||
rpms = {
|
||||
'module-modulea-201808-2-00000000': [
|
||||
[
|
||||
{'name': 'pkg1', 'version': '1.0', 'release': '1.fc28'},
|
||||
{'name': 'pkg2', 'version': '2.34', 'release': '1.fc28'},
|
||||
],
|
||||
],
|
||||
'module-moduleb-7-2-00000000': [
|
||||
[
|
||||
{'name': 'foo', 'version': '2.0', 'release': '1.fc28'},
|
||||
{'name': 'bar', 'version': '1.0', 'release': '1.fc28'},
|
||||
],
|
||||
],
|
||||
|
||||
# These rpms are built for stream collision modules. See above
|
||||
# in setup_method.
|
||||
'module-foo-master-1-00000000': [
|
||||
[
|
||||
{'name': 'foo-a', 'version': '1.0', 'release': '1.fc28'},
|
||||
{'name': 'foo-a-devel', 'version': '1.0', 'release': '1.fc28'},
|
||||
],
|
||||
],
|
||||
'module-bar-master-2-00000000': [
|
||||
[
|
||||
{'name': 'bar-a', 'version': '0.2', 'release': '2.fc28'},
|
||||
{'name': 'bar-a-devel', 'version': '0.2', 'release': '2.fc28'},
|
||||
],
|
||||
]
|
||||
}
|
||||
return rpms[tag]
|
||||
|
||||
get_session.return_value.listTaggedRPMS.side_effect = mocklistTaggedRPMs
|
||||
|
||||
mmd = module_build_service.utils.submit.record_filtered_rpms(self.mmd)
|
||||
xmd_mbs = mmd.get_xmd()['mbs'].unpack()
|
||||
|
||||
assert ['pkg-1.0-1.fc28'] == xmd_mbs['buildrequires']['modulea']['filtered_rpms']
|
||||
assert ['foo-0:2.0-1.fc28'] == xmd_mbs['buildrequires']['moduleb']['filtered_rpms']
|
||||
|
||||
expected_ursine_rpms = [
|
||||
'foo-a-0:1.0-1.fc28', 'foo-a-devel-0:1.0-1.fc28',
|
||||
'bar-a-0:0.2-2.fc28', 'bar-a-devel-0:0.2-2.fc28',
|
||||
]
|
||||
expected_ursine_rpms.sort()
|
||||
|
||||
found_ursine_rpms = xmd_mbs['buildrequires']['platform']['ursine_rpms']
|
||||
found_ursine_rpms.sort()
|
||||
assert expected_ursine_rpms == found_ursine_rpms
|
||||
|
||||
@@ -752,9 +752,10 @@ class TestViews:
|
||||
assert data['meta']['total'] == 0
|
||||
|
||||
@pytest.mark.parametrize('api_version', [1, 2])
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build(self, mocked_scm, mocked_get_user, api_version):
|
||||
def test_submit_build(self, mocked_scm, mocked_get_user, rscm, api_version):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -800,9 +801,10 @@ class TestViews:
|
||||
assert module.buildrequires[0].context == '00000000'
|
||||
assert module.buildrequires[0].stream_version == 280000
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_build_no_base_module(self, mocked_scm, mocked_get_user):
|
||||
def test_submit_build_no_base_module(self, mocked_scm, mocked_get_user, rscm):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule-no-base-module.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -817,11 +819,12 @@ class TestViews:
|
||||
'error': 'Unprocessable Entity'
|
||||
}
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch('module_build_service.config.Config.rebuild_strategy_allow_override',
|
||||
new_callable=PropertyMock, return_value=True)
|
||||
def test_submit_build_rebuild_strategy(self, mocked_rmao, mocked_scm, mocked_get_user):
|
||||
def test_submit_build_rebuild_strategy(self, mocked_rmao, mocked_scm, mocked_get_user, hmsc):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -895,9 +898,10 @@ class TestViews:
|
||||
}
|
||||
assert data == expected_error
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
def test_submit_componentless_build(self, mocked_scm, mocked_get_user):
|
||||
def test_submit_componentless_build(self, mocked_scm, mocked_get_user, rscm):
|
||||
FakeSCM(mocked_scm, 'fakemodule', 'fakemodule.yaml',
|
||||
'3da541559918a808c2402bba5012f6c60b27661c')
|
||||
|
||||
@@ -1119,11 +1123,12 @@ class TestViews:
|
||||
assert result['status'] == 400
|
||||
assert "The request contains 'owner' parameter" in result['message']
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=anonymous_user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch("module_build_service.config.Config.no_auth", new_callable=PropertyMock,
|
||||
return_value=True)
|
||||
def test_submit_build_no_auth_set_owner(self, mocked_conf, mocked_scm, mocked_get_user):
|
||||
def test_submit_build_no_auth_set_owner(self, mocked_conf, mocked_scm, mocked_get_user, hmsc):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -1139,10 +1144,11 @@ class TestViews:
|
||||
build = ModuleBuild.query.filter(ModuleBuild.id == result['id']).one()
|
||||
assert (build.owner == result['owner'] == 'foo') is True
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=anonymous_user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch("module_build_service.config.Config.no_auth", new_callable=PropertyMock)
|
||||
def test_patch_set_different_owner(self, mocked_no_auth, mocked_scm, mocked_get_user):
|
||||
def test_patch_set_different_owner(self, mocked_no_auth, mocked_scm, mocked_get_user, hmsc):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -1183,10 +1189,11 @@ class TestViews:
|
||||
assert data['status'] == 422
|
||||
assert data['error'] == 'Unprocessable Entity'
|
||||
|
||||
@patch('module_build_service.utils.submit.record_stream_collision_modules')
|
||||
@patch('module_build_service.auth.get_user', return_value=user)
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch("module_build_service.config.Config.allow_custom_scmurls", new_callable=PropertyMock)
|
||||
def test_submit_custom_scmurl(self, allow_custom_scmurls, mocked_scm, mocked_get_user):
|
||||
def test_submit_custom_scmurl(self, allow_custom_scmurls, mocked_scm, mocked_get_user, hmsc):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user