Allow setting <kojitag/> in <modules/> in variants.xml to get the modules from this Koji tag.
Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
This commit is contained in:
		
							parent
							
								
									1574f306c7
								
							
						
					
					
						commit
						9be2d6a920
					
				| @ -18,7 +18,9 @@ import os | ||||
| from six.moves import cPickle as pickle | ||||
| import json | ||||
| import re | ||||
| from itertools import groupby | ||||
| from kobo.shortcuts import force_list | ||||
| from kobo.rpmlib import make_nvra | ||||
| 
 | ||||
| import pungi.wrappers.kojiwrapper | ||||
| from pungi.wrappers.comps import CompsWrapper | ||||
| @ -185,6 +187,159 @@ def get_pkgset_from_koji(compose, koji_wrapper, path_prefix): | ||||
|     return package_sets | ||||
| 
 | ||||
| 
 | ||||
| def _add_module_to_variant(variant, mmd, rpms, add_to_variant_modules=False): | ||||
|     """ | ||||
|     Adds module defined by Modulemd.Module `mmd` to variant. | ||||
| 
 | ||||
|     :param Variant variant: Variant to add the module to. | ||||
|     :param Modulemd.Module: Modulemd instance defining the module. | ||||
|     :param list rpms: List of NEVRAs to add to variant along with a module. | ||||
|     :param bool add_to_variant_modules: Adds the modules also to | ||||
|         variant.modules. | ||||
|     """ | ||||
|     # Get the NSVC of module and handle the case where for some reason the | ||||
|     # name/strea/version is not set. | ||||
|     if not mmd.get_name() or not mmd.get_stream() or not mmd.get_version(): | ||||
|         raise ValueError( | ||||
|             "Input module %s does not name or stream or version set." | ||||
|             % mmd.dumps()) | ||||
|     nsvc_list = [mmd.get_name(), mmd.get_stream(), str(mmd.get_version())] | ||||
|     if mmd.get_context(): | ||||
|         nsvc_list.append(mmd.get_context()) | ||||
|     nsvc = ":".join(nsvc_list) | ||||
| 
 | ||||
|     # Catch the issue when build system does not contain RPMs, but | ||||
|     # the module definition says there should be some. | ||||
|     if not rpms and mmd.get_rpm_components(): | ||||
|         raise ValueError( | ||||
|             "Module %s does not have any rpms in 'rpms' in build system," | ||||
|             "but according to modulemd, there should be some." | ||||
|             % nsvc) | ||||
| 
 | ||||
|     # Add RPMs from build systemto modulemd, so we can track | ||||
|     # what RPM is in which module later in gather phase. | ||||
|     rpm_artifacts = mmd.get_rpm_artifacts() | ||||
|     for rpm_nevra in rpms: | ||||
|         if rpm_nevra.endswith(".rpm"): | ||||
|             rpm_nevra = rpm_nevra[:-len(".rpm")] | ||||
|         rpm_artifacts.add(str(rpm_nevra)) | ||||
|     mmd.set_rpm_artifacts(rpm_artifacts) | ||||
|     variant.mmds.append(mmd) | ||||
| 
 | ||||
|     if add_to_variant_modules: | ||||
|         variant.modules.append(nsvc) | ||||
| 
 | ||||
| 
 | ||||
| def _get_modules_from_pdc(compose, session, variant, variant_tags): | ||||
|     """ | ||||
|     Loads modules for given `variant` from PDC `session`, adds them to | ||||
|     the `variant` and also to `variant_tags` dict. | ||||
| 
 | ||||
|     :param Compose compose: Compose for which the modules are found. | ||||
|     :param PDCClient session: PDC session. | ||||
|     :param Variant variant: Variant with modules to find. | ||||
|     :param dict variant_tags: Dict populated by this method. Key is `variant` | ||||
|         and value is list of Koji tags to get the RPMs from. | ||||
|     """ | ||||
|     if not session: | ||||
|         return | ||||
| 
 | ||||
|     # Find out all modules in every variant and add their Koji tags | ||||
|     # to variant and variant_tags list. | ||||
|     for module in variant.get_modules(): | ||||
|         pdc_module = get_module(compose, session, module["name"]) | ||||
| 
 | ||||
|         mmd = Modulemd.Module.new_from_string(pdc_module["modulemd"]) | ||||
|         mmd.upgrade() | ||||
|         _add_module_to_variant(variant, mmd, pdc_module["rpms"]) | ||||
| 
 | ||||
|         tag = pdc_module["koji_tag"] | ||||
|         variant_tags[variant].append(tag) | ||||
| 
 | ||||
|         module_msg = "Module {module} in variant {variant} will use Koji tag {tag}.".format( | ||||
|             variant=variant, tag=tag, module=module["name"]) | ||||
|         compose.log_info("%s" % module_msg) | ||||
| 
 | ||||
| 
 | ||||
| def _get_modules_from_koji_tags( | ||||
|         compose, koji_wrapper, event_id, variant, variant_tags): | ||||
|     """ | ||||
|     Loads modules for given `variant` from Koji, adds them to | ||||
|     the `variant` and also to `variant_tags` dict. | ||||
| 
 | ||||
|     :param Compose compose: Compose for which the modules are found. | ||||
|     :param KojiWrapper koji_wrapper: Koji wrapper. | ||||
|     :param dict event_id: Koji event ID. | ||||
|     :param Variant variant: Variant with modules to find. | ||||
|     :param dict variant_tags: Dict populated by this method. Key is `variant` | ||||
|         and value is list of Koji tags to get the RPMs from. | ||||
|     """ | ||||
|     # Find out all modules in every variant and add their Koji tags | ||||
|     # to variant and variant_tags list. | ||||
|     koji_proxy = koji_wrapper.koji_proxy | ||||
|     for modular_koji_tag in variant.get_modular_koji_tags(): | ||||
|         tag = modular_koji_tag["name"] | ||||
| 
 | ||||
|         # List all the modular builds in the modular Koji tag. | ||||
|         # We cannot use latest=True here, because we need to get all the | ||||
|         # available streams of all modules. The stream is represented as | ||||
|         # "release" in Koji build and with latest=True, Koji would return | ||||
|         # only builds with highest release. | ||||
|         module_builds = koji_proxy.listTagged( | ||||
|             tag, event=event_id["id"], inherit=True, type="module") | ||||
| 
 | ||||
|         # Find the latest builds of all modules. This does following: | ||||
|         # - Sorts the module_builds descending by Koji NVR (which maps to NSV | ||||
|         #   for modules). | ||||
|         # - Groups the sorted module_builds by NV (NS in modular world). | ||||
|         #   In each resulting `ns_group`, the first item is actually build | ||||
|         #   with the latest version (because the list is still sorted by NVR). | ||||
|         # - Groups the `ns_group` again by "release" ("version" in modular | ||||
|         #   world) to just get all the "contexts" of the given NSV. This is | ||||
|         #   stored in `nsv_builds`. | ||||
|         # - The `nsv_builds` contains the builds representing all the contexts | ||||
|         #   of the latest version for give name-stream, so add them to | ||||
|         #   `latest_builds`. | ||||
|         latest_builds = [] | ||||
|         module_builds = sorted( | ||||
|             module_builds, key=lambda build: build['nvr'], reverse=True) | ||||
|         for ns, ns_builds in groupby( | ||||
|                 module_builds, key=lambda x: ":".join([x["name"], x["version"]])): | ||||
|             for nsv, nsv_builds in groupby( | ||||
|                     ns_builds, key=lambda x: x["release"].split(".")[0]): | ||||
|                 latest_builds += list(nsv_builds) | ||||
|                 break | ||||
| 
 | ||||
|         # For each latest modular Koji build, add it to variant and | ||||
|         # variant_tags. | ||||
|         for build in latest_builds: | ||||
|             # Get the Build from Koji to get modulemd and module_tag. | ||||
|             build = koji_proxy.getBuild(build["build_id"]) | ||||
|             module_tag = build.get("extra", {}).get("typeinfo", {}).get( | ||||
|                 "module", {}).get("content_koji_tag", "") | ||||
|             modulemd = build.get("extra", {}).get("typeinfo", {}).get( | ||||
|                 "module", {}).get("modulemd_str", "") | ||||
|             if not module_tag or not modulemd: | ||||
|                 continue | ||||
| 
 | ||||
|             variant_tags[variant].append(module_tag) | ||||
| 
 | ||||
|             # Get the list of all RPMs which are tagged in the modular | ||||
|             # Koji tag for this NSVC and add them to variant. | ||||
|             tagged_rpms = koji_proxy.listTaggedRPMS( | ||||
|                 module_tag, event=event_id["id"], inherit=True, latest=True)[0] | ||||
|             rpms = [make_nvra(rpm, add_epoch=True, force_epoch=True) for rpm in | ||||
|                     tagged_rpms] | ||||
|             mmd = Modulemd.Module.new_from_string(modulemd) | ||||
|             mmd.upgrade() | ||||
|             _add_module_to_variant(variant, mmd, rpms, True) | ||||
| 
 | ||||
|             module_msg = "Module {module} in variant {variant} will use Koji tag {tag}.".format( | ||||
|                 variant=variant, tag=module_tag, module=build["nvr"]) | ||||
|             compose.log_info("%s" % module_msg) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def populate_global_pkgset(compose, koji_wrapper, path_prefix, event_id): | ||||
|     all_arches = set(["src"]) | ||||
|     for arch in compose.get_arches(): | ||||
| @ -226,58 +381,41 @@ def populate_global_pkgset(compose, koji_wrapper, path_prefix, event_id): | ||||
| 
 | ||||
|     session = get_pdc_client_session(compose) | ||||
|     for variant in compose.all_variants.values(): | ||||
|         # pkgset storing the packages belonging to this particular variant. | ||||
|         variant.pkgset = pungi.phases.pkgset.pkgsets.KojiPackageSet( | ||||
|             koji_wrapper, compose.conf["sigkeys"], logger=compose._logger, | ||||
|             arches=all_arches) | ||||
|         variant_tags[variant] = [] | ||||
|         pdc_module_file = os.path.join(compose.paths.work.topdir(arch="global"), | ||||
|                                        "pdc-module-%s.json" % variant.uid) | ||||
|         pdc_modules = [] | ||||
|         # Find out all modules in every variant and add their compose tags | ||||
|         # to compose_tags list. | ||||
|         if session: | ||||
|             for module in variant.get_modules(): | ||||
|                 if not Modulemd: | ||||
| 
 | ||||
|         # Get the modules from Koji tag or from PDC, depending on | ||||
|         # configuration. | ||||
|         modular_koji_tags = variant.get_modular_koji_tags() | ||||
|         if (variant.modules or modular_koji_tags) and not Modulemd: | ||||
|             raise ValueError( | ||||
|                 "pygobject module or libmodulemd library is not installed, " | ||||
|                 "support for modules is disabled, but compose contains " | ||||
|                 "modules.") | ||||
| 
 | ||||
|                 pdc_module = get_module(compose, session, module["name"]) | ||||
|                 pdc_modules.append(pdc_module) | ||||
|                 mmd = Modulemd.Module.new_from_string(pdc_module["modulemd"]) | ||||
|                 mmd.upgrade() | ||||
|         if modular_koji_tags: | ||||
|             included_modules_file = os.path.join( | ||||
|                 compose.paths.work.topdir(arch="global"), | ||||
|                 "koji-tag-module-%s.yaml" % variant.uid) | ||||
|             _get_modules_from_koji_tags( | ||||
|                 compose, koji_wrapper, event_id, variant, variant_tags) | ||||
|         elif variant.modules: | ||||
|             included_modules_file = os.path.join( | ||||
|                 compose.paths.work.topdir(arch="global"), | ||||
|                 "pdc-module-%s.yaml" % variant.uid) | ||||
|             _get_modules_from_pdc(compose, session, variant, variant_tags) | ||||
| 
 | ||||
|                 # Catch the issue when PDC does not contain RPMs, but | ||||
|                 # the module definition says there should be some. | ||||
|                 if not pdc_module["rpms"] and mmd.get_rpm_components(): | ||||
|                     raise ValueError( | ||||
|                         "Module %s does not have any rpms in 'rpms' PDC field," | ||||
|                         "but according to modulemd, there should be some." | ||||
|                         % pdc_module["variant_uid"]) | ||||
|         # Ensure that every tag added to `variant_tags` is added also to | ||||
|         # `compose_tags`. | ||||
|         for variant_tag in variant_tags[variant]: | ||||
|             if not variant_tag in compose_tags: | ||||
|                 compose_tags.append(variant_tag) | ||||
| 
 | ||||
|                 # Add RPMs from PDC response to modulemd, so we can track | ||||
|                 # what RPM is in which module later in gather phase. | ||||
|                 rpm_artifacts = mmd.get_rpm_artifacts() | ||||
|                 for rpm_nevra in pdc_module["rpms"]: | ||||
|                     if rpm_nevra.endswith(".rpm"): | ||||
|                         rpm_nevra = rpm_nevra[:-len(".rpm")] | ||||
|                     rpm_artifacts.add(str(rpm_nevra)) | ||||
|                 mmd.set_rpm_artifacts(rpm_artifacts) | ||||
| 
 | ||||
|                 tag = pdc_module["koji_tag"] | ||||
|                 variant.mmds.append(mmd) | ||||
|                 variant_tags[variant].append(tag) | ||||
|                 if tag not in compose_tags: | ||||
|                     compose_tags.append(tag) | ||||
| 
 | ||||
|                 module_msg = "Module {module} in variant {variant} will use Koji tag {tag}.".format( | ||||
|                     variant=variant, tag=tag, module=module["name"]) | ||||
|                 compose.log_info("%s" % module_msg) | ||||
| 
 | ||||
|         if pdc_modules: | ||||
|             with open(pdc_module_file, 'w') as f: | ||||
|                 json.dump(pdc_modules, f) | ||||
|         if variant.mmds: | ||||
|             Modulemd.Module.dump_all(variant.mmds, included_modules_file) | ||||
|         if not variant_tags[variant] and variant.modules is None: | ||||
|             variant_tags[variant].extend(force_list(compose.conf["pkgset_koji_tag"])) | ||||
| 
 | ||||
|  | ||||
| @ -72,6 +72,7 @@ class VariantsXmlParser(object): | ||||
|             "arches": [str(i) for i in variant_node.xpath("arches/arch/text()")], | ||||
|             "groups": [], | ||||
|             "modules": None, | ||||
|             "modular_koji_tags": None, | ||||
|             "environments": [], | ||||
|             "buildinstallpackages": [], | ||||
|             "is_empty": bool(variant_node.attrib.get("is_empty", False)), | ||||
| @ -113,6 +114,14 @@ class VariantsXmlParser(object): | ||||
| 
 | ||||
|                 variant_dict["modules"].append(module) | ||||
| 
 | ||||
|             for kojitag_node in modulelist_node.xpath("kojitag"): | ||||
|                 kojitag = { | ||||
|                     "name": str(kojitag_node.text), | ||||
|                 } | ||||
| 
 | ||||
|                 variant_dict["modular_koji_tags"] = variant_dict["modular_koji_tags"] or [] | ||||
|                 variant_dict["modular_koji_tags"].append(kojitag) | ||||
| 
 | ||||
|         for environments_node in variant_node.xpath("environments"): | ||||
|             for environment_node in environments_node.xpath("environment"): | ||||
|                 environment = { | ||||
| @ -202,7 +211,7 @@ class VariantsXmlParser(object): | ||||
| class Variant(object): | ||||
|     def __init__(self, id, name, type, arches, groups, environments=None, | ||||
|                  buildinstallpackages=None, is_empty=False, parent=None, | ||||
|                  modules=None): | ||||
|                  modules=None, modular_koji_tags=None): | ||||
| 
 | ||||
|         environments = environments or [] | ||||
|         buildinstallpackages = buildinstallpackages or [] | ||||
| @ -216,6 +225,9 @@ class Variant(object): | ||||
|         self.modules = copy.deepcopy(modules) | ||||
|         if self.modules: | ||||
|             self.modules = sorted(self.modules, key=lambda x: x["name"]) | ||||
|         self.modular_koji_tags = copy.deepcopy(modular_koji_tags) | ||||
|         if self.modular_koji_tags: | ||||
|             self.modular_koji_tags = sorted(self.modular_koji_tags, key=lambda x: x["name"]) | ||||
|         self.buildinstallpackages = sorted(buildinstallpackages) | ||||
|         self.variants = {} | ||||
|         self.parent = parent | ||||
| @ -275,9 +287,9 @@ class Variant(object): | ||||
| 
 | ||||
|         types = types or ["self"] | ||||
|         result = copy.deepcopy(self.groups) | ||||
|         for variant in self.get_variants(arch=arch, types=types, recursive=recursive): | ||||
|         for variant in self.get_variants(arch=arch, types=types, | ||||
|                                          recursive=recursive): | ||||
|             if variant == self: | ||||
|                 # XXX | ||||
|                 continue | ||||
|             for group in variant.get_groups(arch=arch, types=types, recursive=recursive): | ||||
|                 if group not in result: | ||||
| @ -295,7 +307,6 @@ class Variant(object): | ||||
|         for variant in self.get_variants(arch=arch, types=types, | ||||
|                                          recursive=recursive): | ||||
|             if variant == self: | ||||
|                 # XXX | ||||
|                 continue | ||||
|             for module in variant.get_modules(arch=arch, types=types, | ||||
|                                               recursive=recursive): | ||||
| @ -303,6 +314,24 @@ class Variant(object): | ||||
|                     result.append(module) | ||||
|         return result | ||||
| 
 | ||||
|     def get_modular_koji_tags(self, arch=None, types=None, recursive=False): | ||||
|         """Return list of modular koji tags, default types is ["self"]""" | ||||
| 
 | ||||
|         if self.modular_koji_tags is None: | ||||
|             return [] | ||||
| 
 | ||||
|         types = types or ["self"] | ||||
|         result = copy.deepcopy(self.modular_koji_tags) | ||||
|         for variant in self.get_variants(arch=arch, types=types, | ||||
|                                          recursive=recursive): | ||||
|             if variant == self: | ||||
|                 continue | ||||
|             for koji_tag in variant.get_modular_koji_tags( | ||||
|                     arch=arch, types=types, recursive=recursive): | ||||
|                 if koji_tag not in result: | ||||
|                     result.append(koji_tag) | ||||
|         return result | ||||
| 
 | ||||
|     def get_variants(self, arch=None, types=None, recursive=False): | ||||
|         """ | ||||
|         Return all variants of given arch and types. | ||||
|  | ||||
| @ -27,9 +27,10 @@ | ||||
|     uservisible (true|false) #IMPLIED | ||||
| > | ||||
| 
 | ||||
| <!ELEMENT modules (module)*> | ||||
| <!ELEMENT modules (module*, kojitag*)> | ||||
| 
 | ||||
| <!ELEMENT module (#PCDATA)> | ||||
| <!ELEMENT kojitag (#PCDATA)> | ||||
| <!ATTLIST module | ||||
|     glob (true|false) #IMPLIED | ||||
| > | ||||
|  | ||||
| @ -52,6 +52,9 @@ class MockVariant(mock.Mock): | ||||
|     def get_modules(self, arch=None, types=None): | ||||
|         return [] | ||||
| 
 | ||||
|     def get_modular_koji_tags(self, arch=None, types=None): | ||||
|         return [] | ||||
| 
 | ||||
|     def add_fake_module(self, nsvc, rpm_nvrs=None): | ||||
|         if not Modulemd: | ||||
|             # No support for modules | ||||
|  | ||||
| @ -94,7 +94,7 @@ class TestPopulateGlobalPkgset(helpers.PungiTestCase): | ||||
|         self.compose.DEBUG = False | ||||
|         self.koji_wrapper = mock.Mock() | ||||
|         self.pkgset_path = os.path.join(self.topdir, 'work', 'global', 'pkgset_global.pickle') | ||||
|         self.pdc_module_path = os.path.join(self.topdir, 'work', 'global', 'pdc-module-Server.json') | ||||
|         self.pdc_module_path = os.path.join(self.topdir, 'work', 'global', 'pdc-module-Server.yaml') | ||||
| 
 | ||||
|     @mock.patch('six.moves.cPickle.dumps') | ||||
|     @mock.patch('pungi.phases.pkgset.pkgsets.KojiPackageSet') | ||||
| @ -124,22 +124,35 @@ class TestPopulateGlobalPkgset(helpers.PungiTestCase): | ||||
|     @mock.patch('pungi.phases.pkgset.pkgsets.KojiPackageSet') | ||||
|     @mock.patch('pungi.phases.pkgset.sources.source_koji.get_module') | ||||
|     @mock.patch('pungi.phases.pkgset.sources.source_koji.get_pdc_client_session') | ||||
|     @mock.patch('pungi.phases.pkgset.sources.source_koji.Modulemd') | ||||
|     def test_pdc_log(self, modulemd, get_pdc_client_session, get_module, KojiPackageSet, pickle_dumps): | ||||
|     def test_pdc_log(self, get_pdc_client_session, get_module, KojiPackageSet, pickle_dumps): | ||||
| 
 | ||||
|         pickle_dumps.return_value = b'DATA' | ||||
| 
 | ||||
|         get_module.return_value = {'abc': 'def', 'modulemd': 'sth', 'rpms': ['dummy'], 'koji_tag': 'taggg'} | ||||
|         modulemd = """ | ||||
| document: modulemd | ||||
| version: 2 | ||||
| data: | ||||
|     name: foo | ||||
|     stream: bar | ||||
|     version: 1 | ||||
|     summary: foo | ||||
|     description: foo | ||||
|     license: | ||||
|         module: | ||||
|             - MIT | ||||
| """ | ||||
| 
 | ||||
|         get_module.return_value = {'abc': 'def', 'modulemd': modulemd, 'rpms': [], 'koji_tag': 'taggg'} | ||||
|         for name, variant in self.compose.variants.items(): | ||||
|             variant.get_modules = mock.MagicMock() | ||||
|             if name == 'Server': | ||||
|                 variant.get_modules.return_value = [{'name': 'a'}] | ||||
|                 variant.modules = [{'name': 'a'}] | ||||
|                 variant.get_modules.return_value = variant.modules | ||||
| 
 | ||||
|         source_koji.populate_global_pkgset( | ||||
|             self.compose, self.koji_wrapper, '/prefix', 123456) | ||||
|         with open(self.pdc_module_path, 'r') as pdc_f: | ||||
|             self.assertEqual(json.load(pdc_f), | ||||
|                              [{"rpms": ["dummy"], "abc": "def", "koji_tag": "taggg", "modulemd": "sth"}]) | ||||
|         mmds = Modulemd.Module.new_all_from_file(self.pdc_module_path) | ||||
|         self.assertEqual(mmds[0].get_name(), "foo") | ||||
| 
 | ||||
|     @mock.patch('six.moves.cPickle.dumps') | ||||
|     @mock.patch('pungi.phases.pkgset.pkgsets.KojiPackageSet') | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user