Allow including scratch module builds
JIRA: RHELCMP-439 Signed-off-by: Haibo Lin <hlin@redhat.com>
This commit is contained in:
parent
f5e33950c1
commit
f7167fa3b6
@ -190,6 +190,11 @@ Options
|
|||||||
(*str*) -- Allows to set default compose type. Type set via a command-line
|
(*str*) -- Allows to set default compose type. Type set via a command-line
|
||||||
option overwrites this.
|
option overwrites this.
|
||||||
|
|
||||||
|
**mbs_api_url**
|
||||||
|
(*str*) -- URL to Module Build Service (MBS) API.
|
||||||
|
For example ``https://mbs.example.com/module-build-service/2``.
|
||||||
|
This is required by ``pkgset_scratch_modules``.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
::
|
::
|
||||||
@ -548,6 +553,10 @@ Options
|
|||||||
(*dict*) -- A mapping of architectures to repositories with RPMs: ``{arch:
|
(*dict*) -- A mapping of architectures to repositories with RPMs: ``{arch:
|
||||||
[repo]}``. Only use when ``pkgset_source = "repos"``.
|
[repo]}``. Only use when ``pkgset_source = "repos"``.
|
||||||
|
|
||||||
|
**pkgset_scratch_modules**
|
||||||
|
(*dict*) -- A mapping of variants to scratch module builds: ``{variant:
|
||||||
|
[N:S:V:C]}``. Requires ``mbs_api_url``.
|
||||||
|
|
||||||
**pkgset_exclusive_arch_considers_noarch** = True
|
**pkgset_exclusive_arch_considers_noarch** = True
|
||||||
(*bool*) -- If a package includes ``noarch`` in its ``ExclusiveArch`` tag,
|
(*bool*) -- If a package includes ``noarch`` in its ``ExclusiveArch`` tag,
|
||||||
it will be included in all architectures since ``noarch`` is compatible
|
it will be included in all architectures since ``noarch`` is compatible
|
||||||
|
@ -786,6 +786,14 @@ def make_schema():
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": True,
|
"default": True,
|
||||||
},
|
},
|
||||||
|
"pkgset_scratch_modules": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^.+$": {"$ref": "#/definitions/list_of_strings"}
|
||||||
|
},
|
||||||
|
"additionalProperties": False,
|
||||||
|
},
|
||||||
|
"mbs_api_url": {"type": "string"},
|
||||||
"disc_types": {"type": "object", "default": {}},
|
"disc_types": {"type": "object", "default": {}},
|
||||||
"paths_module": {"type": "string"},
|
"paths_module": {"type": "string"},
|
||||||
"skip_phases": {
|
"skip_phases": {
|
||||||
@ -1258,6 +1266,7 @@ CONFIG_DEPS = {
|
|||||||
"conflicts": ((lambda x: not x, ["base_product_name", "base_product_short"]),),
|
"conflicts": ((lambda x: not x, ["base_product_name", "base_product_short"]),),
|
||||||
},
|
},
|
||||||
"product_id": {"conflicts": [(lambda x: not x, ["product_id_allow_missing"])]},
|
"product_id": {"conflicts": [(lambda x: not x, ["product_id_allow_missing"])]},
|
||||||
|
"pkgset_scratch_modules": {"requires": ((lambda x: x, ["mbs_api_url"]),)},
|
||||||
"pkgset_source": {
|
"pkgset_source": {
|
||||||
"requires": [(lambda x: x == "repos", ["pkgset_repos"])],
|
"requires": [(lambda x: x == "repos", ["pkgset_repos"])],
|
||||||
"conflicts": [
|
"conflicts": [
|
||||||
|
@ -490,6 +490,8 @@ class WorkPaths(object):
|
|||||||
|
|
||||||
def module_defaults_dir(self, create_dir=True):
|
def module_defaults_dir(self, create_dir=True):
|
||||||
"""
|
"""
|
||||||
|
Example:
|
||||||
|
work/global/module_defaults
|
||||||
"""
|
"""
|
||||||
path = os.path.join(self.topdir(create_dir=create_dir), "module_defaults")
|
path = os.path.join(self.topdir(create_dir=create_dir), "module_defaults")
|
||||||
if create_dir:
|
if create_dir:
|
||||||
|
@ -26,9 +26,10 @@ from kobo.shortcuts import force_list
|
|||||||
|
|
||||||
import pungi.wrappers.kojiwrapper
|
import pungi.wrappers.kojiwrapper
|
||||||
from pungi.wrappers.comps import CompsWrapper
|
from pungi.wrappers.comps import CompsWrapper
|
||||||
|
from pungi.wrappers.mbs import MBSWrapper
|
||||||
import pungi.phases.pkgset.pkgsets
|
import pungi.phases.pkgset.pkgsets
|
||||||
from pungi.arch import getBaseArch
|
from pungi.arch import getBaseArch
|
||||||
from pungi.util import retry, get_arch_variant_data
|
from pungi.util import retry, get_arch_variant_data, get_variant_data
|
||||||
from pungi.module_util import Modulemd
|
from pungi.module_util import Modulemd
|
||||||
|
|
||||||
from pungi.phases.pkgset.common import MaterializedPackageSet, get_all_arches
|
from pungi.phases.pkgset.common import MaterializedPackageSet, get_all_arches
|
||||||
@ -272,6 +273,51 @@ def _add_module_to_variant(
|
|||||||
return nsvc
|
return nsvc
|
||||||
|
|
||||||
|
|
||||||
|
def _add_scratch_modules_to_variant(
|
||||||
|
compose, variant, scratch_modules, variant_tags, tag_to_mmd
|
||||||
|
):
|
||||||
|
if compose.compose_type != "test" and scratch_modules:
|
||||||
|
compose.log_warning("Only test composes could include scratch module builds")
|
||||||
|
return
|
||||||
|
|
||||||
|
mbs = MBSWrapper(compose.conf["mbs_api_url"])
|
||||||
|
for nsvc in scratch_modules:
|
||||||
|
module_build = mbs.get_module_build_by_nsvc(nsvc)
|
||||||
|
if not module_build:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
final_modulemd = mbs.final_modulemd(module_build["id"])
|
||||||
|
except Exception:
|
||||||
|
compose.log_error("Unable to get modulemd for build %s" % module_build)
|
||||||
|
raise
|
||||||
|
tag = module_build["koji_tag"]
|
||||||
|
variant_tags[variant].append(tag)
|
||||||
|
tag_to_mmd.setdefault(tag, {})
|
||||||
|
for arch in variant.arches:
|
||||||
|
try:
|
||||||
|
mmd = Modulemd.ModuleStream.read_string(
|
||||||
|
final_modulemd[arch], strict=True
|
||||||
|
)
|
||||||
|
variant.arch_mmds.setdefault(arch, {})[nsvc] = mmd
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
tag_to_mmd[tag].setdefault(arch, set()).add(mmd)
|
||||||
|
|
||||||
|
if tag_to_mmd[tag]:
|
||||||
|
compose.log_info(
|
||||||
|
"Module '%s' in variant '%s' will use Koji tag '%s' "
|
||||||
|
"(as a result of querying module '%s')",
|
||||||
|
nsvc,
|
||||||
|
variant,
|
||||||
|
tag,
|
||||||
|
module_build["name"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Store mapping NSVC --> koji_tag into variant. This is needed
|
||||||
|
# in createrepo phase where metadata is exposed by productmd
|
||||||
|
variant.module_uid_to_koji_tag[nsvc] = tag
|
||||||
|
|
||||||
|
|
||||||
def _is_filtered_out(compose, variant, arch, module_name, module_stream):
|
def _is_filtered_out(compose, variant, arch, module_name, module_stream):
|
||||||
"""Check if module with given name and stream is filter out from this stream.
|
"""Check if module with given name and stream is filter out from this stream.
|
||||||
"""
|
"""
|
||||||
@ -618,6 +664,14 @@ def populate_global_pkgset(compose, koji_wrapper, path_prefix, event):
|
|||||||
compose, koji_wrapper, event, variant, variant_tags, tag_to_mmd
|
compose, koji_wrapper, event, variant, variant_tags, tag_to_mmd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
variant_scratch_modules = get_variant_data(
|
||||||
|
compose.conf, "pkgset_scratch_modules", variant
|
||||||
|
)
|
||||||
|
if variant_scratch_modules:
|
||||||
|
_add_scratch_modules_to_variant(
|
||||||
|
compose, variant, variant_scratch_modules, variant_tags, tag_to_mmd
|
||||||
|
)
|
||||||
|
|
||||||
# Ensure that every tag added to `variant_tags` is added also to
|
# Ensure that every tag added to `variant_tags` is added also to
|
||||||
# `compose_tags`.
|
# `compose_tags`.
|
||||||
for variant_tag in variant_tags[variant]:
|
for variant_tag in variant_tags[variant]:
|
||||||
|
43
pungi/wrappers/mbs.py
Normal file
43
pungi/wrappers/mbs.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class MBSWrapper(object):
|
||||||
|
def __init__(self, api_url):
|
||||||
|
"""
|
||||||
|
:param string api_url: e.g. https://example.com/module-build-service/2
|
||||||
|
"""
|
||||||
|
self.api_url = api_url
|
||||||
|
|
||||||
|
def _get(self, resource, params=None):
|
||||||
|
"""Get specified resource.
|
||||||
|
|
||||||
|
:param string resource: e.g. module-builds, final-modulemd
|
||||||
|
:param dict data:
|
||||||
|
"""
|
||||||
|
url = os.path.join(self.api_url, resource)
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, params=params)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(
|
||||||
|
"Failed to query URL %s with params %s - %s" % (url, params, str(e))
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def module_builds(self, filters=None):
|
||||||
|
return self._get("module-builds", filters).json()
|
||||||
|
|
||||||
|
def get_module_build_by_nsvc(self, nsvc):
|
||||||
|
nsvc_list = nsvc.split(":")
|
||||||
|
if len(nsvc_list) != 4:
|
||||||
|
raise ValueError("Invalid N:S:V:C - %s" % nsvc)
|
||||||
|
filters = dict(zip(["name", "stream", "version", "context"], nsvc_list))
|
||||||
|
resp = self.module_builds(filters)
|
||||||
|
if resp["items"]:
|
||||||
|
return resp["items"][0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def final_modulemd(self, module_build_id):
|
||||||
|
return self._get("final-modulemd/%s" % module_build_id).json()
|
@ -881,3 +881,68 @@ class TestIsModuleFiltered(helpers.PungiTestCase):
|
|||||||
self.assertIsFiltered("foo", "master")
|
self.assertIsFiltered("foo", "master")
|
||||||
self.assertIsNotFiltered("bar", "master")
|
self.assertIsNotFiltered("bar", "master")
|
||||||
self.assertIsNotFiltered("foo", "stable")
|
self.assertIsNotFiltered("foo", "stable")
|
||||||
|
|
||||||
|
|
||||||
|
class MockMBS(object):
|
||||||
|
def __init__(self, api_url):
|
||||||
|
self.api_url = api_url
|
||||||
|
|
||||||
|
def get_module_build_by_nsvc(self, nsvc):
|
||||||
|
return {"id": 1, "koji_tag": "scratch-module-tag", "name": "scratch-module"}
|
||||||
|
|
||||||
|
def final_modulemd(self, module_build_id):
|
||||||
|
return {"x86_64": ""}
|
||||||
|
|
||||||
|
|
||||||
|
class MockMmd(object):
|
||||||
|
def __init__(self, mmd, strict=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch("pungi.phases.pkgset.sources.source_koji.MBSWrapper", new=MockMBS)
|
||||||
|
@unittest.skipIf(Modulemd is None, "Skipping tests, no module support")
|
||||||
|
class TestAddScratchModuleToVariant(helpers.PungiTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestAddScratchModuleToVariant, self).setUp()
|
||||||
|
self.compose = helpers.DummyCompose(
|
||||||
|
self.topdir, {"mbs_api_url": "http://mbs.local/module-build-service/2"}
|
||||||
|
)
|
||||||
|
self.nsvc = "scratch-module:master:20200710:abcdef"
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
"pungi.phases.pkgset.sources.source_koji.Modulemd.ModuleStream.read_string"
|
||||||
|
)
|
||||||
|
def test_adding_scratch_module(self, mock_mmd):
|
||||||
|
variant = mock.Mock(
|
||||||
|
arches=["armhfp", "x86_64"],
|
||||||
|
arch_mmds={},
|
||||||
|
modules=[],
|
||||||
|
module_uid_to_koji_tag={},
|
||||||
|
)
|
||||||
|
variant_tags = {variant: []}
|
||||||
|
tag_to_mmd = {}
|
||||||
|
scratch_modules = [self.nsvc]
|
||||||
|
|
||||||
|
source_koji._add_scratch_modules_to_variant(
|
||||||
|
self.compose, variant, scratch_modules, variant_tags, tag_to_mmd
|
||||||
|
)
|
||||||
|
self.assertEqual(variant_tags, {variant: ["scratch-module-tag"]})
|
||||||
|
self.assertEqual(
|
||||||
|
variant.arch_mmds, {"x86_64": {self.nsvc: mock_mmd.return_value}}
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
tag_to_mmd, {"scratch-module-tag": {"x86_64": {mock_mmd.return_value}}}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(variant.modules, [])
|
||||||
|
|
||||||
|
def test_adding_scratch_module_nontest_compose(self):
|
||||||
|
self.compose.compose_type = "production"
|
||||||
|
scratch_modules = [self.nsvc]
|
||||||
|
|
||||||
|
source_koji._add_scratch_modules_to_variant(
|
||||||
|
self.compose, mock.Mock(), scratch_modules, {}, {}
|
||||||
|
)
|
||||||
|
self.compose.log_warning.assert_called_once_with(
|
||||||
|
"Only test composes could include scratch module builds"
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user