def test_get_reusable_component_different_buildrequires_stream( self, rebuild_strategy): first_module_build = models.ModuleBuild.get_by_id(db_session, 2) first_module_build.rebuild_strategy = rebuild_strategy db_session.commit() second_module_build = models.ModuleBuild.get_by_id(db_session, 3) mmd = second_module_build.mmd() xmd = mmd.get_xmd() xmd["mbs"]["buildrequires"]["platform"]["stream"] = "different" deps = Modulemd.Dependencies() deps.add_buildtime_stream("platform", "different") deps.add_runtime_stream("platform", "different") mmd.clear_dependencies() mmd.add_dependencies(deps) mmd.set_xmd(xmd) second_module_build.modulemd = mmd_to_str(mmd) second_module_build.build_context = \ models.ModuleBuild.contexts_from_mmd(second_module_build.modulemd).build_context second_module_build.rebuild_strategy = rebuild_strategy db_session.commit() plc_rv = get_reusable_component(second_module_build, "perl-List-Compare") pt_rv = get_reusable_component(second_module_build, "perl-Tangerine") tangerine_rv = get_reusable_component(second_module_build, "tangerine") assert plc_rv is None assert pt_rv is None assert tangerine_rv is None
def test__get_base_module_mmds_virtual_streams(self, virtual_streams): """Ensure the correct results are returned without duplicates.""" init_data(data_size=1, multiple_stream_versions=True) mmd = load_mmd(read_staged_data("testmodule_v2")) deps = mmd.get_dependencies()[0] new_deps = Modulemd.Dependencies() for stream in deps.get_runtime_streams("platform"): new_deps.add_runtime_stream("platform", stream) new_deps.add_buildtime_stream("platform", "f29.2.0") mmd.remove_dependencies(deps) mmd.add_dependencies(new_deps) make_module_in_db("platform:lp29.1.1:12:c11", virtual_streams=virtual_streams) mmds = get_base_module_mmds(db_session, mmd) if virtual_streams == ["f29"]: expected = { "platform:f29.0.0", "platform:f29.1.0", "platform:f29.2.0", "platform:lp29.1.1" } else: expected = { "platform:f29.0.0", "platform:f29.1.0", "platform:f29.2.0" } # Verify no duplicates were returned before doing set operations assert len(mmds["ready"]) == len(expected) # Verify the expected ones were returned actual = set() for mmd_ in mmds["ready"]: actual.add("{}:{}".format(mmd_.get_module_name(), mmd_.get_stream_name())) assert actual == expected
def test_get_module_build_dependencies_recursive( self, reuse_component_init_data): """ Tests that the buildrequires are returned when it is two layers deep """ # Add testmodule2 that requires testmodule module = models.ModuleBuild.get_by_id(db_session, 3) mmd = module.mmd() # Rename the module mmd = mmd.copy("testmodule2") mmd.set_version(20180123171545) deps = Modulemd.Dependencies() deps.add_runtime_stream("testmodule", "master") mmd.add_dependencies(deps) xmd = mmd.get_xmd() xmd["mbs"]["requires"]["testmodule"] = { "filtered_rpms": [], "ref": "620ec77321b2ea7b0d67d82992dda3e1d67055b4", "stream": "master", "version": "20180205135154", } mmd.set_xmd(xmd) module.modulemd = mmd_to_str(mmd) module.name = "testmodule2" module.version = str(mmd.get_version()) module.koji_tag = "module-ae2adf69caf0e1b6" db_session.commit() resolver = mbs_resolver.GenericResolver.create(db_session, conf, backend="db") result = resolver.get_module_build_dependencies( "testmodule2", "master", "20180123171545", "c40c156c").keys() assert set(result) == {"module-f28-build"}
def submit_module_build_from_yaml( db_session, username, handle, params, stream=None, skiptests=False ): yaml_file = to_text_type(handle.read()) mmd = load_mmd(yaml_file) dt = datetime.utcfromtimestamp(int(time.time())) if hasattr(handle, "filename"): def_name = str(os.path.splitext(os.path.basename(handle.filename))[0]) elif not mmd.get_module_name(): raise ValidationError( "The module's name was not present in the modulemd file. Please use the " '"module_name" parameter' ) def_version = int(dt.strftime("%Y%m%d%H%M%S")) module_name = mmd.get_module_name() or def_name module_stream = stream or mmd.get_stream_name() or "master" if module_name != mmd.get_module_name() or module_stream != mmd.get_stream_name(): # This is how you set the name and stream in the modulemd mmd = mmd.copy(module_name, module_stream) mmd.set_version(mmd.get_version() or def_version) if skiptests: buildopts = mmd.get_buildopts() or Modulemd.Buildopts() macros = buildopts.get_rpm_macros() or "" buildopts.set_rpm_macros(macros + "\n\n%__spec_check_pre exit 0\n") mmd.set_buildopts(buildopts) return submit_module_build(db_session, username, mmd, params)
def test_fill_in_rpms_rpm_whitelist(self, devel): self._add_test_rpm( "python27-dhcp-12:4.3.5-5.module_2118aef6.src", "dhcp-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="python27-dhcp-12:4.3.5-5.module_2118aef6.src", ) self._add_test_rpm( "python27-dhcp-libs-12:4.3.5-5.module_2118aef6.x86_64", "dhcp-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="python27-dhcp-12:4.3.5-5.module_2118aef6.src", ) self._add_test_rpm( "python27-dhcp-libs-12:4.3.5-5.module_2118aef6.i686", "dhcp-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="python27-dhcp-12:4.3.5-5.module_2118aef6.src", ) self._add_test_rpm( "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.src", "perl-Tangerine-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.src", ) self._add_test_rpm( "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.x86_64", "perl-Tangerine-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.src", ) self._add_test_rpm( "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.i686", "perl-Tangerine-12:4.3.5-5.module_2118aef6.src", koji_srpm_nevra="foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.src", ) self.cg.devel = devel mmd = self.cg.module.mmd() opts = mmd.get_buildopts() if not opts: opts = Modulemd.Buildopts() opts.add_rpm_to_whitelist("python27-dhcp") mmd.set_buildopts(opts) mmd = self.cg._fill_in_rpms_list(mmd, "x86_64") if not devel: # Only x86_64 dhcp-libs should be filled in, because only python27-dhcp is whitelisted # srpm name. assert set(mmd.get_rpm_artifacts()) == { "python27-dhcp-12:4.3.5-5.module_2118aef6.src", "python27-dhcp-libs-12:4.3.5-5.module_2118aef6.x86_64", } else: assert set(mmd.get_rpm_artifacts()) == { "python27-dhcp-libs-12:4.3.5-5.module_2118aef6.i686", "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.src", "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.x86_64", "foo-perl-Tangerine-12:4.3.5-5.module_2118aef6.i686", }
def mmd_to_str(mmd): """ Helper method to convert a Modulemd.ModuleStream object to a YAML string. :param Modulemd.ModuleStream mmd: the modulemd to convert :return: the YAML string of the modulemd :rtype: str """ index = Modulemd.ModuleIndex() index.add_module_stream(mmd) return to_text_type(index.dump_to_string())
def test_get_reusable_component_different_rpm_macros(self): second_module_build = models.ModuleBuild.get_by_id(db_session, 3) mmd = second_module_build.mmd() buildopts = Modulemd.Buildopts() buildopts.set_rpm_macros("%my_macro 1") mmd.set_buildopts(buildopts) second_module_build.modulemd = mmd_to_str(mmd) db_session.commit() plc_rv = get_reusable_component(second_module_build, "perl-List-Compare") assert plc_rv is None pt_rv = get_reusable_component(second_module_build, "perl-Tangerine") assert pt_rv is None
def expand_mse_streams(db_session, mmd, default_streams=None, raise_if_stream_ambigous=False): """ Expands streams in both buildrequires/requires sections of MMD. :param db_session: SQLAlchemy DB session. :param Modulemd.ModuleStream mmd: Modulemd metadata with original unexpanded module. :param dict default_streams: Dict in {module_name: module_stream, ...} format defining the default stream to choose for module in case when there are multiple streams to choose from. :param bool raise_if_stream_ambigous: When True, raises a StreamAmbigous exception in case there are multiple streams for some dependency of module and the module name is not defined in `default_streams`, so it is not clear which stream should be used. """ for deps in mmd.get_dependencies(): new_deps = Modulemd.Dependencies() for name in deps.get_runtime_modules(): streams = deps.get_runtime_streams(name) new_streams = expand_single_mse_streams(db_session, name, streams, default_streams, raise_if_stream_ambigous) if not new_streams: new_deps.set_empty_runtime_dependencies_for_module(name) else: for stream in new_streams: new_deps.add_runtime_stream(name, stream) for name in deps.get_buildtime_modules(): streams = deps.get_buildtime_streams(name) new_streams = expand_single_mse_streams(db_session, name, streams, default_streams, raise_if_stream_ambigous) if not new_streams: new_deps.set_empty_buildtime_dependencies_for_module(name) else: for stream in new_streams: new_deps.add_buildtime_stream(name, stream) # Replace the Dependencies object mmd.remove_dependencies(deps) mmd.add_dependencies(new_deps)
def test_get_build_arches_set_in_mmd(self): mmd = load_mmd(read_staged_data("formatted_testmodule")) xmd = mmd.get_xmd() mbs_options = xmd.get("mbs", {}) mbs_options["buildrequires"] = {"platform": {"stream": "xx"}} xmd["mbs"] = mbs_options mmd.set_xmd(xmd) try: opts = Modulemd.Buildopts() opts.add_arch("x86_64") mmd.set_buildopts(opts) expected_result = ["x86_64"] except AttributeError: # libmodulemd version < 2.8.3 expected_result = ["x86_64", "i686"] r = get_build_arches(mmd, conf) assert r == expected_result
def _modify_buildtime_streams(db_session, mmd, new_streams_func): """ Modify buildtime streams using the input new_streams_func. :param Modulemd.ModuleStream mmd: the modulemd to apply the overrides on :param function new_streams: a function that takes the parameters (module_name, module_streams), and returns the streams that should be set on the buildtime dependency. """ deps = mmd.get_dependencies() for dep in deps: overridden = False brs = deps_to_dict(dep, "buildtime") # There is no way to replace streams, so create a new Dependencies object that will end up # being a copy, but with the streams replaced if a virtual stream is detected new_dep = Modulemd.Dependencies() for name, streams in brs.items(): new_streams = new_streams_func(db_session, name, streams) if streams != new_streams: overridden = True if not new_streams: new_dep.set_empty_buildtime_dependencies_for_module(name) else: for stream in new_streams: new_dep.add_buildtime_stream(name, stream) if overridden: # Copy the runtime streams as is reqs = deps_to_dict(dep, "runtime") for name, streams in reqs.items(): if not streams: new_dep.set_empty_runtime_dependencies_for_module(name) else: for stream in streams: new_dep.add_runtime_stream(name, stream) # Replace the old Dependencies object with the new one with the overrides mmd.remove_dependencies(dep) mmd.add_dependencies(new_dep)
def test__get_base_module_mmds_virtual_streams_only_major_versions( self, cfg): """Ensure the correct results are returned without duplicates.""" init_data(data_size=1, multiple_stream_versions=["foo28", "foo29", "foo30"]) # Mark platform:foo28 as garbage to test that it is still considered as compatible. platform = db_session.query(models.ModuleBuild).filter_by( name="platform", stream="foo28").first() platform.state = "garbage" db_session.add(platform) db_session.commit() mmd = load_mmd(read_staged_data("testmodule_v2")) deps = mmd.get_dependencies()[0] new_deps = Modulemd.Dependencies() for stream in deps.get_runtime_streams("platform"): new_deps.add_runtime_stream("platform", stream) new_deps.add_buildtime_stream("platform", "foo29") mmd.remove_dependencies(deps) mmd.add_dependencies(new_deps) mmds = get_base_module_mmds(db_session, mmd) expected = {} expected["ready"] = {"platform:foo29", "platform:foo30"} expected["garbage"] = {"platform:foo28"} # Verify no duplicates were returned before doing set operations assert len(mmds) == len(expected) for k in expected.keys(): assert len(mmds[k]) == len(expected[k]) # Verify the expected ones were returned actual = set() for mmd_ in mmds[k]: actual.add("{}:{}".format(mmd_.get_module_name(), mmd_.get_stream_name())) assert actual == expected[k]
def make_module( nsvc, dependencies=None, base_module=None, filtered_rpms=None, xmd=None, store_to_db=False, virtual_streams=None, arches=None, ): """ Creates new models.ModuleBuild defined by `nsvc` string with requires and buildrequires set according to ``requires_list`` and ``build_requires_list``. :param str nsvc: name:stream:version:context of a module. :param dependencies: list of groups of dependencies (requires and buildrequires). For example, [ {"requires": {"platform": ["f30"]}, "buildrequires": {"platform": ["f30"]}}, ... ] :type dependencies: list[dict] :param base_module: a base module build required by the new module created. :type base_module: :class:`ModuleBuild` :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 testing a particular scenario and some default key/value pairs are added if not present. :param bool store_to_db: whether to store created module metadata to the database. If set to True, ``db_session`` is required. :param virtual_streams: List of virtual streams provided by this module. If set, This requires ``db_session`` and ``store_to_db`` to be set to a session object and True. :type virtual_streams: list[str] :param arches: List of architectures this module is built against. If set to None, ``["x86_64"]`` is used as a default. If set, likewise ``virtual_stream``. :type arches: list[str] :return: New Module Build if set to store module metadata to database, otherwise the module metadata is returned. :rtype: ModuleBuild or Modulemd.Module """ if store_to_db: assert db_session is not None if base_module: assert db_session is not None if virtual_streams: assert store_to_db if arches: assert store_to_db nsvc_regex = re.compile(r"([^:]+):([^:]+):([^:]+)(?::(.+))?") match = nsvc_regex.match(nsvc) if not match: raise ValueError('Argument nsvc is not in format N:S:V or N:S:V:C') name, stream, version, context = match.groups() mmd = Modulemd.ModuleStreamV2.new(name, stream) mmd.set_version(int(version)) if context: mmd.set_context(context) mmd.set_summary("foo") # Test unicode in mmd. mmd.set_description(u"foo \u2019s") mmd.add_module_license("GPL") if filtered_rpms: for rpm in filtered_rpms: mmd.add_rpm_filter(rpm) def _add_require(mmd_deps, require_type, name, streams): assert isinstance(mmd_deps, Modulemd.Dependencies) assert require_type in ("requires", "buildrequires") assert isinstance(streams, (list, tuple)) if require_type == "requires": add_stream = mmd_deps.add_runtime_stream set_empty_deps = mmd_deps.set_empty_runtime_dependencies_for_module else: add_stream = mmd_deps.add_buildtime_stream set_empty_deps = mmd_deps.set_empty_buildtime_dependencies_for_module for stream in streams: add_stream(name, stream) else: set_empty_deps(name) for dep_group in dependencies or []: mmd_deps = Modulemd.Dependencies() # A deps could be {"platform": ["f30"], "python": []} for require_type, deps in dep_group.items(): for req_name, req_streams in deps.items(): _add_require(mmd_deps, require_type, req_name, req_streams) mmd.add_dependencies(mmd_deps) # 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" if virtual_streams: xmd_mbs["virtual_streams"] = virtual_streams mmd.set_xmd(xmd) if not store_to_db: return mmd module_build = ModuleBuild( name=name, stream=stream, stream_version=ModuleBuild.get_stream_version(stream), version=version, context=context, state=BUILD_STATES["ready"], scmurl="https://src.stg.fedoraproject.org/modules/unused.git?#ff1ea79", batch=1, owner="Tom Brady", time_submitted=datetime(2017, 2, 15, 16, 8, 18), time_modified=datetime(2017, 2, 15, 16, 19, 35), rebuild_strategy="changed-and-after", build_context=context, runtime_context=context, modulemd=mmd_to_str(mmd), koji_tag=xmd["mbs"]["koji_tag"] if "koji_tag" in xmd["mbs"] else None, ) if base_module: module_build.buildrequires.append(base_module) db_session.add(module_build) db_session.commit() if virtual_streams: for virtual_stream in virtual_streams: vs_obj = db_session.query(VirtualStream).filter_by( name=virtual_stream).first() if not vs_obj: vs_obj = VirtualStream(name=virtual_stream) db_session.add(vs_obj) db_session.commit() if vs_obj not in module_build.virtual_streams: module_build.virtual_streams.append(vs_obj) db_session.commit() for arch in arches or ["x86_64"]: arch_obj = db_session.query(ModuleArch).filter_by(name=arch).first() if not arch_obj: arch_obj = ModuleArch(name=arch) db_session.add(arch_obj) db_session.commit() if arch_obj not in module_build.arches: module_build.arches.append(arch_obj) db_session.commit() return module_build
def __get_tools(self): """Return list of tools which are important for reproducing mbs outputs""" return [{"name": "libmodulemd", "version": Modulemd.get_version()}]
def test_buildroot_connect(self, custom_whitelist, blocklist, repo_include_all, mock_get_session): module_build = module_build_service.common.models.ModuleBuild.get_by_id( db_session, 2) db_session.refresh(module_build) if blocklist: mmd = module_build.mmd() xmd = mmd.get_xmd() xmd["mbs_options"] = {"blocked_packages": ["foo", "nginx"]} mmd.set_xmd(xmd) module_build.modulemd = mmd_to_str(mmd) db_session.commit() if custom_whitelist: mmd = module_build.mmd() opts = Modulemd.Buildopts() opts.add_rpm_to_whitelist("custom1") opts.add_rpm_to_whitelist("custom2") mmd.set_buildopts(opts) module_build.modulemd = mmd_to_str(mmd) else: # Set some irrelevant buildopts options to test that KojiModuleBuilder # is not confused by this. mmd = module_build.mmd() opts = Modulemd.Buildopts() opts.set_rpm_macros("%my_macro 1") mmd.set_buildopts(opts) module_build.modulemd = mmd_to_str(mmd) db_session.commit() if repo_include_all is False: mmd = module_build.mmd() xmd = mmd.get_xmd() mbs_options = xmd["mbs_options"] if "mbs_options" in xmd.keys( ) else {} mbs_options["repo_include_all"] = False xmd["mbs_options"] = mbs_options mmd.set_xmd(xmd) module_build.modulemd = mmd_to_str(mmd) db_session.commit() module_build.arches.append( module_build_service.common.models.ModuleArch(name="i686")) db_session.commit() builder = FakeKojiModuleBuilder( db_session=db_session, owner=module_build.owner, module=module_build, config=conf, tag_name="module-foo", components=["nginx"], ) session = builder.koji_session groups = OrderedDict() groups["build"] = {"unzip"} groups["srpm-build"] = {"fedora-release"} builder.buildroot_connect(groups) if custom_whitelist: expected_calls = [ mock.call("module-foo", "custom1", "Moe Szyslak"), mock.call("module-foo", "custom2", "Moe Szyslak"), mock.call("module-foo-build", "custom1", "Moe Szyslak"), mock.call("module-foo-build", "custom2", "Moe Szyslak"), ] else: expected_calls = [ mock.call("module-foo", "nginx", "Moe Szyslak"), mock.call("module-foo-build", "nginx", "Moe Szyslak"), ] assert session.packageListAdd.mock_calls == expected_calls expected_calls = [ mock.call("module-foo-build", "build"), mock.call("module-foo-build", "srpm-build"), ] assert session.groupListAdd.mock_calls == expected_calls expected_calls = [ mock.call("module-foo-build", "build", "unzip"), mock.call("module-foo-build", "srpm-build", "fedora-release"), ] assert session.groupPackageListAdd.mock_calls == expected_calls # packageListBlock should not be called, because we set the block list only when creating # new Koji tag to prevent overriding it on each buildroot_connect. expected_calls = [] assert session.packageListBlock.mock_calls == expected_calls expected_arches = "i686 x86_64" expected_calls = [ mock.call( "module-foo", arches=expected_arches, extra={ "mock.package_manager": "dnf", "repo_include_all": repo_include_all, "mock.new_chroot": 0, 'mock.yum.module_hotfixes': 1, }, ), mock.call( "module-foo-build", arches=expected_arches, extra={ "mock.package_manager": "dnf", "repo_include_all": repo_include_all, "mock.new_chroot": 0, 'mock.yum.module_hotfixes': 1, }, ), ] assert session.editTag2.mock_calls == expected_calls
def _apply_dep_overrides(mmd, params): """ Apply the dependency override parameters (if specified) on the input modulemd. :param Modulemd.ModuleStream mmd: the modulemd to apply the overrides on :param dict params: the API parameters passed in by the user :raises ValidationError: if one of the overrides doesn't apply """ dep_overrides = { "buildrequires": copy.copy(params.get("buildrequire_overrides", {})), "requires": copy.copy(params.get("require_overrides", {})), } # Parse the module's branch to determine if it should override the stream of the buildrequired # module defined in conf.br_stream_override_module branch_search = None if params.get("branch") and conf.br_stream_override_module and conf.br_stream_override_regexes: # Only parse the branch for a buildrequire override if the user didn't manually specify an # override for the module specified in conf.br_stream_override_module if not dep_overrides["buildrequires"].get(conf.br_stream_override_module): branch_search = None for regex in conf.br_stream_override_regexes: branch_search = re.search(regex, params["branch"]) if branch_search: log.debug( "The stream override regex `%s` matched the branch %s", regex, params["branch"], ) break else: log.debug('No stream override regexes matched the branch "%s"', params["branch"]) # If a stream was parsed from the branch, then add it as a stream override for the module # specified in conf.br_stream_override_module if branch_search: # Concatenate all the groups that are not None together to get the desired stream. # This approach is taken in case there are sections to ignore. # For instance, if we need to parse `el8.0.0` from `rhel-8.0.0`. parsed_stream = "".join(group for group in branch_search.groups() if group) if parsed_stream: dep_overrides["buildrequires"][conf.br_stream_override_module] = [parsed_stream] log.info( 'The buildrequired stream of "%s" was overriden with "%s" based on the branch "%s"', conf.br_stream_override_module, parsed_stream, params["branch"], ) else: log.warning( 'The regex `%s` only matched empty capture groups on the branch "%s". The regex is ' " invalid and should be rewritten.", regex, params["branch"], ) unused_dep_overrides = { "buildrequires": set(dep_overrides["buildrequires"].keys()), "requires": set(dep_overrides["requires"].keys()), } deps = mmd.get_dependencies() for dep in deps: overridden = False new_dep = Modulemd.Dependencies() for dep_type, overrides in dep_overrides.items(): if dep_type == "buildrequires": mmd_dep_type = "buildtime" else: mmd_dep_type = "runtime" # Get the existing streams reqs = deps_to_dict(dep, mmd_dep_type) # Get the method to add a new stream for this dependency type # (e.g. add_buildtime_stream) add_func = getattr(new_dep, "add_{}_stream".format(mmd_dep_type)) add_empty_func = getattr( new_dep, "set_empty_{}_dependencies_for_module".format(mmd_dep_type)) for name, streams in reqs.items(): if name in dep_overrides[dep_type]: streams_to_add = dep_overrides[dep_type][name] unused_dep_overrides[dep_type].remove(name) overridden = True else: streams_to_add = reqs[name] if not streams_to_add: add_empty_func(name) else: for stream in streams_to_add: add_func(name, stream) if overridden: # Set the overridden streams mmd.remove_dependencies(dep) mmd.add_dependencies(new_dep) for dep_type in unused_dep_overrides.keys(): # If a stream override was applied from parsing the branch and it wasn't applicable, # just ignore it if branch_search and conf.br_stream_override_module in unused_dep_overrides[dep_type]: unused_dep_overrides[dep_type].remove(conf.br_stream_override_module) if unused_dep_overrides[dep_type]: raise ValidationError( "The {} overrides for the following modules aren't applicable: {}".format( dep_type[:-1], ", ".join(sorted(unused_dep_overrides[dep_type]))) )
def generate_expanded_mmds(db_session, mmd, raise_if_stream_ambigous=False, default_streams=None): """ Returns list with MMDs with buildrequires and requires set according to module stream expansion rules. These module metadata can be directly built using MBS. :param db_session: SQLAlchemy DB session. :param Modulemd.ModuleStream mmd: Modulemd metadata with original unexpanded module. :param bool raise_if_stream_ambigous: When True, raises a StreamAmbigous exception in case there are multiple streams for some dependency of module and the module name is not defined in `default_streams`, so it is not clear which stream should be used. :param dict default_streams: Dict in {module_name: module_stream, ...} format defining the default stream to choose for module in case when there are multiple streams to choose from. """ if not default_streams: default_streams = {} # Create local copy of mmd, because we will expand its dependencies, # which would change the module. current_mmd = mmd.copy() # MMDResolver expects the input MMD to have no context. current_mmd.set_context(None) # Expands the MSE streams. This mainly handles '-' prefix in MSE streams. expand_mse_streams(db_session, current_mmd, default_streams, raise_if_stream_ambigous) # Get the list of all MMDs which this module can be possibly built against # and add them to MMDResolver. mmd_resolver = MMDResolver() mmds_for_resolving = get_mmds_required_by_module_recursively( db_session, current_mmd, default_streams, raise_if_stream_ambigous) for m in mmds_for_resolving: mmd_resolver.add_modules(m) # Show log.info message with the NSVCs we have added to mmd_resolver. nsvcs_to_solve = [m.get_nsvc() for m in mmds_for_resolving] log.info("Starting resolving with following input modules: %r", nsvcs_to_solve) # Resolve the dependencies between modules and get the list of all valid # combinations in which we can build this module. requires_combinations = mmd_resolver.solve(current_mmd) log.info("Resolving done, possible requires: %r", requires_combinations) # This is where we are going to store the generated MMDs. mmds = [] for requires in requires_combinations: # Each generated MMD must be new Module object... mmd_copy = mmd.copy() xmd = mmd_copy.get_xmd() # Requires contain the NSVC representing the input mmd. # The 'context' of this NSVC defines the id of buildrequires/requires # pair in the mmd.get_dependencies(). dependencies_id = None # We don't want to depend on ourselves, so store the NSVC of the current_mmd # to be able to ignore it later. self_nsvca = None # Dict to store name:stream pairs from nsvca, so we are able to access it # easily later. req_name_stream = {} # Get the values for dependencies_id, self_nsvca and req_name_stream variables. for nsvca in requires: req_name, req_stream, _, req_context, req_arch = nsvca.split(":") if req_arch == "src": assert req_name == current_mmd.get_module_name() assert req_stream == current_mmd.get_stream_name() assert dependencies_id is None assert self_nsvca is None dependencies_id = int(req_context) self_nsvca = nsvca continue req_name_stream[req_name] = req_stream if dependencies_id is None or self_nsvca is None: raise RuntimeError("%s:%s not found in requires %r" % (current_mmd.get_module_name(), current_mmd.get_stream_name(), requires)) # The name:[streams, ...] pairs do not have to be the same in both # buildrequires/requires. In case they are the same, we replace the streams # in requires section with a single stream against which we will build this MMD. # In case they are not the same, we have to keep the streams as they are in requires # section. We always replace stream(s) for build-requirement with the one we # will build this MMD against. new_deps = Modulemd.Dependencies() deps = mmd_copy.get_dependencies()[dependencies_id] deps_requires = deps_to_dict(deps, 'runtime') deps_buildrequires = deps_to_dict(deps, 'buildtime') for req_name, req_streams in deps_requires.items(): if req_name not in deps_buildrequires: # This require is not a buildrequire so just copy this runtime requirement to # new_dep and don't touch buildrequires if not req_streams: new_deps.set_empty_runtime_dependencies_for_module( req_name) else: for req_stream in req_streams: new_deps.add_runtime_stream(req_name, req_stream) elif set(req_streams) != set(deps_buildrequires[req_name]): # Streams in runtime section are not the same as in buildtime section, # so just copy this runtime requirement to new_dep. if not req_streams: new_deps.set_empty_runtime_dependencies_for_module( req_name) else: for req_stream in req_streams: new_deps.add_runtime_stream(req_name, req_stream) new_deps.add_buildtime_stream(req_name, req_name_stream[req_name]) else: # This runtime requirement has the same streams in both runtime/buildtime # requires sections, so replace streams in both sections by the one we # really used in this resolved variant. new_deps.add_runtime_stream(req_name, req_name_stream[req_name]) new_deps.add_buildtime_stream(req_name, req_name_stream[req_name]) # There might be buildrequires which are not in runtime requires list. # Such buildrequires must be copied to expanded MMD. for req_name, req_streams in deps_buildrequires.items(): if req_name not in deps_requires: new_deps.add_buildtime_stream(req_name, req_name_stream[req_name]) # Set the new dependencies. mmd_copy.remove_dependencies(deps) mmd_copy.add_dependencies(new_deps) # The Modulemd.Dependencies() stores only streams, but to really build this # module, we need NSVC of buildrequires, so we have to store this data in XMD. # We also need additional data like for example list of filtered_rpms. We will # get them using module_build_service.resolver.GenericResolver.resolve_requires, # so prepare list with NSVCs of buildrequires as an input for this method. br_list = [] for nsvca in requires: if nsvca == self_nsvca: continue # Remove the arch from nsvca nsvc = ":".join(nsvca.split(":")[:-1]) br_list.append(nsvc) # Resolve the buildrequires and store the result in XMD. if "mbs" not in xmd: xmd["mbs"] = {} resolver = GenericResolver.create(db_session, conf) xmd["mbs"]["buildrequires"] = resolver.resolve_requires(br_list) xmd["mbs"]["mse"] = True mmd_copy.set_xmd(xmd) # Now we have all the info to actually compute context of this module. context = models.ModuleBuild.contexts_from_mmd( mmd_to_str(mmd_copy)).context mmd_copy.set_context(context) mmds.append(mmd_copy) return mmds