mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-11-03 13:55:17 +08:00
Factor out packaging utility
Until now, it was assumed that the module-build command returned only one build, so it was only one build_id. However, it is possible that the module-build command will build more than one builds and therefore a list of build_ids is needed. Also is needed to watch and cancel more than one build. For this reason run, watch, and cancel methods are methods of the PackagingUtility class instead of Build class. Run method returns list of Build objects instead of build_id. And it's also possible to cancel and to watch on all generated module builds.
This commit is contained in:
@@ -28,6 +28,16 @@ def test_env():
|
||||
return env
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def pkg_util(test_env):
|
||||
"""Fixture to interact with the packaging utility
|
||||
|
||||
:return: Packaging utility configured for the tests
|
||||
:rtype: object of utils.PackagingUtility
|
||||
"""
|
||||
return utils.PackagingUtility(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def scenario(request, test_env):
|
||||
"""Configuration data for the scenario
|
||||
|
||||
@@ -64,3 +64,8 @@ testdata:
|
||||
no_components:
|
||||
module: testmodule
|
||||
branch: test-no-components-branch
|
||||
stream_expansion:
|
||||
# For this scenario it's necessary to use testmodule2 as a module and it doesn't make sense
|
||||
# reusing a former build
|
||||
module: testmodule2
|
||||
branch: test-stream-expans-branch
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test_failed_build(test_env, scenario, repo, koji):
|
||||
def test_failed_build(pkg_util, scenario, repo, koji):
|
||||
"""
|
||||
Run the build with "rebuild_strategy=all".
|
||||
|
||||
@@ -13,14 +11,15 @@ def test_failed_build(test_env, scenario, repo, koji):
|
||||
* Check that any other components in the same batch as the failed component are
|
||||
cancelled, if not completed.
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
reuse=scenario.get("build_id"),
|
||||
)
|
||||
build.watch()
|
||||
assert len(builds) == 1
|
||||
build = builds[0]
|
||||
pkg_util.watch(builds)
|
||||
|
||||
assert build.state_name == "failed"
|
||||
batch = scenario["batch"]
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test_no_components(test_env, scenario, repo, koji):
|
||||
def test_no_components(pkg_util, scenario, repo, koji):
|
||||
"""
|
||||
Submit the testmodule build with `fedpkg module-build`
|
||||
|
||||
@@ -13,10 +11,12 @@ def test_no_components(test_env, scenario, repo, koji):
|
||||
* Verify that the testmodule build succeeds
|
||||
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build.run(reuse=scenario.get("build_id"))
|
||||
build.watch()
|
||||
builds = pkg_util.run(reuse=scenario.get("build_id"))
|
||||
assert len(builds) == 1
|
||||
|
||||
pkg_util.watch(builds)
|
||||
build = builds[0]
|
||||
|
||||
assert build.state_name == "ready"
|
||||
assert not build.data["component_builds"]
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test_normal_build(test_env, scenario, repo, koji):
|
||||
def test_normal_build(pkg_util, scenario, repo, koji):
|
||||
"""
|
||||
Run build with `rhpkg-stage module-build --optional rebuild_strategy=all`
|
||||
|
||||
@@ -17,14 +15,15 @@ def test_normal_build(test_env, scenario, repo, koji):
|
||||
* Check that MBS changed the buildrequired platform to have a suffix of “z”
|
||||
if a Platform stream is representing a GA RHEL release.
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build_id = build.run(
|
||||
builds = pkg_util.run(
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
reuse=scenario.get("build_id"),
|
||||
)
|
||||
build.watch()
|
||||
assert len(builds) == 1
|
||||
pkg_util.watch(builds)
|
||||
build = builds[0]
|
||||
|
||||
assert sorted(build.component_names()) == sorted(repo.components + ["module-build-macros"])
|
||||
|
||||
@@ -36,7 +35,7 @@ def test_normal_build(test_env, scenario, repo, koji):
|
||||
cg_build = koji.get_build(build.nvr())
|
||||
cg_devel_build = koji.get_build(build.nvr(name_suffix="-devel"))
|
||||
assert cg_build and cg_devel_build
|
||||
assert cg_devel_build['extra']['typeinfo']['module']['module_build_service_id'] == int(build_id)
|
||||
assert cg_devel_build['extra']['typeinfo']['module']['module_build_service_id'] == int(build.id)
|
||||
|
||||
modulemd = koji.get_modulemd(cg_build)
|
||||
actual_platforms = modulemd["data"]["dependencies"][0]["buildrequires"]["platform"]
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
import time
|
||||
|
||||
|
||||
def test_resume_cancelled_build(test_env, scenario, repo, koji):
|
||||
def test_resume_cancelled_build(pkg_util, scenario, repo, koji):
|
||||
"""
|
||||
Run the build with "rebuild_strategy=all".
|
||||
Wait until the module-build-macros build is submitted to Koji.
|
||||
@@ -17,17 +16,20 @@ def test_resume_cancelled_build(test_env, scenario, repo, koji):
|
||||
* Check that the testmodule build succeeded
|
||||
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
)
|
||||
|
||||
assert len(builds) == 1
|
||||
build = builds[0]
|
||||
build.wait_for_koji_task_id(package="module-build-macros", batch=1)
|
||||
build.cancel()
|
||||
pkg_util.cancel(build)
|
||||
# Behave like a human: restarting the build too quickly would lead to an error.
|
||||
time.sleep(10)
|
||||
build.run("--watch")
|
||||
|
||||
builds = pkg_util.run("--watch")
|
||||
assert len(builds) == 1
|
||||
build = builds[0]
|
||||
assert build.state_name == "ready"
|
||||
assert build.was_cancelled()
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test_reuse_all_components(test_env, scenario, repo, koji):
|
||||
def test_reuse_all_components(pkg_util, scenario, repo, koji):
|
||||
"""Rebuild the test module again, without changing any of the components with:
|
||||
|
||||
`fedpkg module-build -w --optional rebuild_strategy=only-changed`
|
||||
@@ -13,23 +11,28 @@ def test_reuse_all_components(test_env, scenario, repo, koji):
|
||||
* Verify that all the components are reused from the first build.
|
||||
* Verify that module-build-macros is not built in the second build.
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"--watch",
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
reuse=scenario.get("build_id"),
|
||||
)
|
||||
assert len(builds) == 1
|
||||
|
||||
build = builds[0]
|
||||
task_ids = build.component_task_ids()
|
||||
task_ids.pop("module-build-macros")
|
||||
|
||||
repo.bump()
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"-w",
|
||||
"--optional",
|
||||
"rebuild_strategy=only-changed",
|
||||
reuse=scenario.get("build_id_reused"))
|
||||
|
||||
assert len(builds) == 1
|
||||
build = builds[0]
|
||||
reused_task_ids = build.component_task_ids()
|
||||
|
||||
assert not build.components(package="module-build-macros")
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import utils
|
||||
|
||||
|
||||
def test_reuse_components(test_env, scenario, repo, koji):
|
||||
def test_reuse_components(pkg_util, test_env, scenario, repo, koji):
|
||||
"""
|
||||
Bump the commit of one of the components that MBS uses.
|
||||
Bump the commit of the same testmodule that was mentioned in the preconditions.
|
||||
@@ -16,14 +16,15 @@ def test_reuse_components(test_env, scenario, repo, koji):
|
||||
* Verify that the component with the changed commit was rebuilt.
|
||||
"""
|
||||
repo.bump()
|
||||
baseline_build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
baseline_build.run(
|
||||
baseline_builds = pkg_util.run(
|
||||
"--watch",
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
reuse=scenario.get("baseline_build_id"),
|
||||
)
|
||||
assert len(baseline_builds) == 1
|
||||
|
||||
baseline_build = baseline_builds[0]
|
||||
package = scenario.get("package")
|
||||
component = utils.Component(
|
||||
package,
|
||||
@@ -33,14 +34,14 @@ def test_reuse_components(test_env, scenario, repo, koji):
|
||||
component.bump()
|
||||
|
||||
repo.bump()
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"--watch",
|
||||
"--optional",
|
||||
"rebuild_strategy=only-changed",
|
||||
reuse=scenario.get("build_id"),
|
||||
)
|
||||
|
||||
assert len(builds) == 1
|
||||
build = builds[0]
|
||||
comp_task_ids_base = baseline_build.component_task_ids()
|
||||
comp_task_ids = build.component_task_ids()
|
||||
comp_task_ids_base.pop('module-build-macros')
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
def test_scratch_build(test_env, scenario, repo, koji):
|
||||
def test_scratch_build(pkg_util, scenario, repo, koji):
|
||||
"""
|
||||
Run a scratch build with "rebuild_strategy=all".
|
||||
|
||||
@@ -14,14 +12,16 @@ def test_scratch_build(test_env, scenario, repo, koji):
|
||||
(as opposed to the "ready" state)
|
||||
* no content generator builds are created in Koji
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
build.run(
|
||||
builds = pkg_util.run(
|
||||
"--scratch",
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
reuse=scenario.get("build_id"),
|
||||
)
|
||||
build.watch()
|
||||
|
||||
assert len(builds) == 1
|
||||
pkg_util.watch(builds)
|
||||
build = builds[0]
|
||||
|
||||
assert build.state_name == "done"
|
||||
assert sorted(build.component_names(state="COMPLETE")) == sorted(
|
||||
|
||||
@@ -114,15 +114,12 @@ class Repo:
|
||||
git("push")
|
||||
|
||||
|
||||
class Build:
|
||||
"""Wrapper class to work with module builds
|
||||
class PackagingUtility:
|
||||
"""Wrapper class to work with the packaging utility configured for the tests
|
||||
|
||||
:attribute sh.Command _packaging_utility: packaging utility command used to
|
||||
kick off this build
|
||||
:attribute string _mbs_api: URL of the MBS API (including trailing '/')
|
||||
:attribute string _url: URL of this MBS module build
|
||||
:attribute string _data: Module build data cache for this build fetched from MBS
|
||||
:attribute string _module_build_data: Verbose module build data cache for this build
|
||||
"""
|
||||
|
||||
def __init__(self, packaging_utility, mbs_api):
|
||||
@@ -130,54 +127,76 @@ class Build:
|
||||
_out=sys.stdout, _err=sys.stderr, _tee=True
|
||||
)
|
||||
self._mbs_api = mbs_api
|
||||
self._data = None
|
||||
self._component_data = None
|
||||
self._build_id = None
|
||||
self._module_build_data = None
|
||||
|
||||
def run(self, *args, reuse=None):
|
||||
"""Run a module build
|
||||
"""Run one or more module builds
|
||||
|
||||
:param args: Options and arguments for the build command
|
||||
:param int reuse: Optional MBS build id to be reused for this run.
|
||||
When specified, the corresponding module build will be used,
|
||||
instead of triggering and waiting for a new one to finish.
|
||||
:param int reuse: An optional MBS build id or a list of MBS build
|
||||
ids to be reused for this run.
|
||||
When specified, the corresponding module build(s) will be used,
|
||||
instead of triggering and waiting for new one(s) to finish.
|
||||
Intended to be used while developing the tests.
|
||||
:return: MBS build id of the build created
|
||||
:rtype: int
|
||||
:return: list of Build objects for the MBS builds created
|
||||
:rtype: list of Build objects
|
||||
"""
|
||||
current_build_id = self._build_id
|
||||
build_ids = []
|
||||
|
||||
if reuse is not None:
|
||||
self._build_id = int(reuse)
|
||||
if isinstance(reuse, list):
|
||||
build_ids = reuse
|
||||
else:
|
||||
build_ids = [reuse]
|
||||
else:
|
||||
stdout = self._packaging_utility("module-build", *args).stdout.decode("utf-8")
|
||||
self._build_id = int(re.search(self._mbs_api + r"module-builds/(\d+)", stdout).group(1))
|
||||
# Clear cached data
|
||||
if current_build_id != self._build_id:
|
||||
self._component_data = None
|
||||
return self._build_id
|
||||
build_ids = re.findall(self._mbs_api + r"module-builds/(\d+)", stdout)
|
||||
return [Build(self._mbs_api, int(build_id)) for build_id in build_ids]
|
||||
|
||||
def watch(self):
|
||||
"""Watch the build till the finish"""
|
||||
if self._build_id is None:
|
||||
raise RuntimeError("Build was not started. Cannot watch.")
|
||||
def watch(self, builds):
|
||||
"""Watch one or more builds till the finish
|
||||
|
||||
:param list builds: list of Build objects of the builds to be watched.
|
||||
:return: Stdout of the watch command
|
||||
:rtype: string
|
||||
"""
|
||||
stdout = self._packaging_utility(
|
||||
"module-build-watch", str(self._build_id)
|
||||
"module-build-watch", [str(build.id) for build in builds]
|
||||
).stdout.decode("utf-8")
|
||||
|
||||
return stdout
|
||||
|
||||
def cancel(self):
|
||||
def cancel(self, build):
|
||||
"""Cancel the module build
|
||||
|
||||
:param list build: the Build object of the module build to be cancelled.
|
||||
:return: Standard output of the "module-build-cancel <build id=""> command
|
||||
:rtype: str
|
||||
"""
|
||||
stdout = self._packaging_utility("module-build-cancel", self._build_id).stdout.decode(
|
||||
stdout = self._packaging_utility("module-build-cancel", build.id).stdout.decode(
|
||||
"utf-8")
|
||||
return stdout
|
||||
|
||||
|
||||
class Build:
|
||||
"""Wrapper class to work with module builds
|
||||
|
||||
:attribute string _mbs_api: URL of the MBS API (including trailing '/')
|
||||
:attribute int _build_id: id of this MBS module build
|
||||
:attribute string _data: Module build data cache for this build fetched from MBS
|
||||
:attribute string _module_build_data: Verbose module build data cache for this build
|
||||
"""
|
||||
|
||||
def __init__(self, mbs_api, build_id):
|
||||
self._mbs_api = mbs_api
|
||||
self._data = None
|
||||
self._component_data = None
|
||||
self._build_id = build_id
|
||||
self._module_build_data = None
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._build_id
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""Module build data cache for this build fetched from MBS"""
|
||||
|
||||
Reference in New Issue
Block a user