Allow specifying a specific module build to reuse components from

This resolves #1296.
This commit is contained in:
mprahl
2019-07-15 17:22:02 -04:00
parent 6c4d6ee2b4
commit 1bcb40d4d0
6 changed files with 156 additions and 1 deletions

View File

@@ -128,6 +128,8 @@ Options:
rebuilt). For the available options, please look at the "Rebuild Strategies" section below.
- ``require_overrides`` - the requires to override the modulemd with. The overrides must be to
existing requires on the modulemd. The expected format is ``{'platform': ['f28', 'f29']}``.
- ``reuse_components_from`` - the ID or NSVC of the module build to reuse components from. If it's
not set, MBS will try to find a compatible module build to reuse components from.
- ``scratch`` - a boolean indicating if a scratch module build should be performed.
Only allowed to be ``True`` if the MBS setting ``MODULES_ALLOW_SCRATCH`` is ``True``.
- ``srpms`` - an optional list of Koji upload URLs of SRPMs to include in a module scratch build.
@@ -357,6 +359,7 @@ parameters::
"modulemd": "...."
"name": "testmodule",
"owner": "mprahl",
"reused_module_id": 121,
"scmurl": "https://src.fedoraproject.org/modules/testmodule.git?#86d9cfe53d20118d863ae051641fc3784d91d981",
"state": 5,
"state_name": "ready",
@@ -479,6 +482,7 @@ parameters include:
- ``new_repo_task_id``
- ``owner``
- ``rebuild_strategy``
- ``reuse_components_from`` - the compatible module that was used for component reuse
- ``scmurl``
- ``state`` - Can be the state name or the state ID e.g. ``state=done``. This
parameter can be given multiple times, in which case or-ing will be used.

View File

@@ -75,6 +75,9 @@ module with the following requirements:
Additionally, if the rebuild strategy for the module being built is ``changed-and-after``, then the
module to reuse components from will have a rebuild strategy of ``changed-and-after`` or ``all``.
If the user wants to specify the compatible module, they can use the ``reuse_components_from``
parameter.
How the Rebuild Strategies Work
===============================

View File

@@ -877,6 +877,7 @@ class ModuleBuild(MBSBase):
"build_context": self.build_context,
"modulemd": self.modulemd,
"ref_build_context": self.ref_build_context,
"reused_module_id": self.reused_module_id,
"runtime_context": self.runtime_context,
"state_trace": [
{

View File

@@ -1014,6 +1014,7 @@ def submit_module_build(username, mmd, params):
scmurl=params.get("scmurl"),
username=username,
rebuild_strategy=params.get("rebuild_strategy"),
reused_module_id=params.get("reuse_components_from"),
scratch=params.get("scratch"),
srpms=params.get("srpms"),
)

View File

@@ -305,6 +305,7 @@ class BaseHandler(object):
"module_name",
"owner",
"rebuild_strategy",
"reuse_components_from",
"require_overrides",
"scmurl",
"scratch",
@@ -382,6 +383,40 @@ class BaseHandler(object):
self._validate_dep_overrides_format("buildrequire_overrides")
self._validate_dep_overrides_format("require_overrides")
if "reuse_components_from" in self.data:
if "rebuild_strategy" in self.data and self.data["rebuild_strategy"] == "all":
raise ValidationError(
'You cannot specify the parameter "reuse_components_from" when the '
'"rebuild_strategy" parameter is set to "all"'
)
invalid_identifier_msg = (
'The parameter "reuse_components_from" contains an invalid module identifier')
if isinstance(self.data["reuse_components_from"], int):
reuse_module = models.ModuleBuild.get_by_id(
db.session, self.data["reuse_components_from"])
elif isinstance(self.data["reuse_components_from"], string_types):
try:
n, s, v, c = self.data["reuse_components_from"].split(":")
except ValueError:
raise ValidationError(invalid_identifier_msg)
reuse_module = models.ModuleBuild.get_build_from_nsvc(db.session, n, s, v, c)
else:
raise ValidationError(invalid_identifier_msg)
if not reuse_module:
raise ValidationError(
'The module in the parameter "reuse_components_from" could not be found')
if reuse_module.state != models.BUILD_STATES["ready"]:
raise ValidationError(
'The module in the parameter "reuse_components_from" must be in the ready state'
)
# Normalize the value so that it simplifies any code that uses this value
self.data["reuse_components_from"] = reuse_module.id
class SCMHandler(BaseHandler):
def validate(self, skip_branch=False, skip_optional_params=False):

View File

@@ -31,6 +31,7 @@ from os import path, mkdir
from os.path import basename, dirname, splitext
from requests.utils import quote
import hashlib
import koji
import pytest
import re
import sqlalchemy
@@ -39,7 +40,7 @@ from tests import app, init_data, clean_database, reuse_component_init_data, sta
from tests import read_staged_data
from tests.test_scm import base_dir as scm_base_dir
from module_build_service.errors import UnprocessableEntity
from module_build_service.models import ModuleBuild
from module_build_service.models import ModuleBuild, BUILD_STATES
from module_build_service import db, version
import module_build_service.config as mbs_config
import module_build_service.scheduler.handlers.modules
@@ -207,6 +208,7 @@ class TestViews:
assert data["name"] == "nginx"
assert data["owner"] == "Moe Szyslak"
assert data["rebuild_strategy"] == "changed-and-after"
assert data["reused_module_id"] is None
assert data["scmurl"] == \
"git://pkgs.domain.local/modules/nginx?#ba95886c7a443b36a9ce31abda1f9bef22f2f8c9"
assert data["scratch"] is False
@@ -2592,3 +2594,112 @@ class TestViews:
mock_get.assert_called_once_with(expected_url, timeout=15)
else:
mock_get.assert_not_called()
@pytest.mark.parametrize("reuse_components_from", (7, "testmodule:4.3.43:7:00000000"))
@patch("module_build_service.auth.get_user", return_value=user)
@patch("module_build_service.scm.SCM")
def test_submit_build_reuse_components_from(
self, mocked_scm, mocked_get_user, reuse_components_from,
):
"""Test a build submission using the reuse_components_from parameter."""
module_to_reuse = db.session.query(ModuleBuild).get(7)
module_to_reuse.state = BUILD_STATES["ready"]
for c in module_to_reuse.component_builds:
c.state = koji.BUILD_STATES["COMPLETE"]
db.session.commit()
FakeSCM(
mocked_scm, "testmodule", "testmodule.yaml", "620ec77321b2ea7b0d67d82992dda3e1d67055b4")
rv = self.client.post(
"/module-build-service/1/module-builds/",
data=json.dumps({
"branch": "master",
"reuse_components_from": reuse_components_from,
"scmurl": (
"https://src.stg.fedoraproject.org/modules/testmodule.git?"
"#68931c90de214d9d13feefbd35246a81b6cb8d49"
),
}),
)
data = json.loads(rv.data)
assert data["reused_module_id"] == 7
@pytest.mark.parametrize(
"reuse_components_from, expected_error",
(
(
"testmodule:4.3.43:7",
'The parameter "reuse_components_from" contains an invalid module identifier',
),
(
{},
'The parameter "reuse_components_from" contains an invalid module identifier',
),
(
912312312,
'The module in the parameter "reuse_components_from" could not be found',
),
(
7,
'The module in the parameter "reuse_components_from" must be in the ready state',
)
)
)
@patch("module_build_service.auth.get_user", return_value=user)
@patch("module_build_service.scm.SCM")
def test_submit_build_reuse_components_from_errors(
self, mocked_scm, mocked_get_user, reuse_components_from, expected_error,
):
"""
Test a build submission using an invalid value for the reuse_components_from parameter.
"""
FakeSCM(
mocked_scm, "testmodule", "testmodule.yaml", "620ec77321b2ea7b0d67d82992dda3e1d67055b4")
rv = self.client.post(
"/module-build-service/1/module-builds/",
data=json.dumps({
"branch": "master",
"reuse_components_from": reuse_components_from,
"scmurl": (
"https://src.stg.fedoraproject.org/modules/testmodule.git?"
"#68931c90de214d9d13feefbd35246a81b6cb8d49"
),
}),
)
data = json.loads(rv.data)
assert rv.status_code == 400
assert data["message"] == expected_error
@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_reuse_components_rebuild_strategy_all(
self, mock_rsao, mocked_scm, mocked_get_user,
):
"""
Test a build submission using reuse_components_from and the rebuild_strategy of all.
"""
FakeSCM(
mocked_scm, "testmodule", "testmodule.yaml", "620ec77321b2ea7b0d67d82992dda3e1d67055b4")
rv = self.client.post(
"/module-build-service/1/module-builds/",
data=json.dumps({
"branch": "master",
"rebuild_strategy": "all",
"reuse_components_from": 7,
"scmurl": (
"https://src.stg.fedoraproject.org/modules/testmodule.git?"
"#68931c90de214d9d13feefbd35246a81b6cb8d49"
),
}),
)
data = json.loads(rv.data)
assert rv.status_code == 400
assert data["message"] == (
'You cannot specify the parameter "reuse_components_from" when the "rebuild_strategy" '
'parameter is set to "all"'
)