mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-04-04 03:08:21 +08:00
Integration test for the Cancel And Resume
Implement the integration test for the Cancel and Resume scenario.
This commit is contained in:
@@ -48,9 +48,6 @@ def repo(request, test_env):
|
||||
args = [
|
||||
"--branch",
|
||||
repo_conf["branch"],
|
||||
"--single-branch",
|
||||
"--depth",
|
||||
"1",
|
||||
url,
|
||||
tempdir,
|
||||
]
|
||||
|
||||
@@ -22,7 +22,7 @@ testdata:
|
||||
module: testmodule
|
||||
# Branch which is going to be built for this test.
|
||||
branch: scratch-build-branch
|
||||
failed_build:
|
||||
failed_build:
|
||||
module: testmodule
|
||||
branch: failed-build-branch
|
||||
# Batch considered by this test.
|
||||
@@ -40,3 +40,6 @@ failed_build:
|
||||
buildorder: [{"module-build-macros"}, {"attr"}, {"acl"}]
|
||||
# True if buildrequire a Platform stream representing a GA RHEL release
|
||||
platform_is_ga: true
|
||||
resume_cancelled_build:
|
||||
module: testmodule
|
||||
branch: cancel-build-branch
|
||||
|
||||
@@ -6,7 +6,7 @@ import utils
|
||||
|
||||
def test_failed_build(test_env, repo, koji):
|
||||
"""
|
||||
Run a scratch build with "rebuild_strategy=all".
|
||||
Run the build with "rebuild_strategy=all".
|
||||
|
||||
Check that:
|
||||
* Check that the module build eventually fails
|
||||
@@ -26,8 +26,8 @@ def test_failed_build(test_env, repo, koji):
|
||||
batch = test_env["testdata"]["failed_build"]["batch"]
|
||||
failing_components = test_env["testdata"]["failed_build"]["failing_components"]
|
||||
canceled_components = test_env["testdata"]["failed_build"]["canceled_components"]
|
||||
assert sorted(failing_components) == sorted(build.components(state="FAILED", batch=batch))
|
||||
assert sorted(failing_components) == sorted(build.component_names(state="FAILED", batch=batch))
|
||||
assert sorted(canceled_components) == sorted(
|
||||
build.components(state="COMPLETE", batch=batch)
|
||||
+ build.components(state="CANCELED", batch=batch)
|
||||
build.component_names(state="COMPLETE", batch=batch)
|
||||
+ build.component_names(state="CANCELED", batch=batch)
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ def test_normal_build(test_env, repo, koji):
|
||||
"rebuild_strategy=all",
|
||||
reuse=test_env["testdata"]["normal_build"].get("build_id"),
|
||||
)
|
||||
assert sorted(build.components()) == sorted(repo.components + ["module-build-macros"])
|
||||
assert sorted(build.component_names()) == sorted(repo.components + ["module-build-macros"])
|
||||
|
||||
expected_buildorder = test_env["testdata"]["normal_build"]["buildorder"]
|
||||
expected_buildorder = [set(batch) for batch in expected_buildorder]
|
||||
|
||||
33
tests/integration/test_resume_cancelled_build.py
Normal file
33
tests/integration/test_resume_cancelled_build.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import utils
|
||||
import time
|
||||
|
||||
|
||||
def test_resume_cancelled_build(test_env, repo, koji):
|
||||
"""
|
||||
Run the build with "rebuild_strategy=all".
|
||||
Wait until the module-build-macros build is submitted to Koji.
|
||||
Cancel module build.
|
||||
Resume the module with "rhpkg-stage module-build -w".
|
||||
|
||||
Check that:
|
||||
* Check that the testmodule had actually been cancelled
|
||||
* Check that the testmodule build succeeded
|
||||
|
||||
"""
|
||||
build = utils.Build(test_env["packaging_utility"], test_env["mbs_api"])
|
||||
repo.bump()
|
||||
build.run(
|
||||
"--optional",
|
||||
"rebuild_strategy=all",
|
||||
)
|
||||
build.wait_for_koji_task_id(package="module-build-macros", batch=1)
|
||||
build.cancel()
|
||||
# Behave like a human: restarting the build too quickly would lead to an error.
|
||||
time.sleep(10)
|
||||
build.run("--watch")
|
||||
|
||||
assert build.state_name == "ready"
|
||||
assert build.was_cancelled()
|
||||
@@ -24,7 +24,7 @@ def test_scratch_build(test_env, repo, koji):
|
||||
)
|
||||
|
||||
assert build.state_name == "done"
|
||||
assert sorted(build.components(state="COMPLETE")) == sorted(
|
||||
assert sorted(build.component_names(state="COMPLETE")) == sorted(
|
||||
repo.components + ["module-build-macros"]
|
||||
)
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
from kobo import rpmlib
|
||||
import koji
|
||||
import yaml
|
||||
import requests
|
||||
from sh import Command
|
||||
from sh import Command, git
|
||||
|
||||
|
||||
class Koji:
|
||||
@@ -99,6 +100,16 @@ class Repo:
|
||||
elif self._version == 2:
|
||||
return self._modulemd["data"]["dependencies"][0]["buildrequires"].get("platform")
|
||||
|
||||
def bump(self):
|
||||
"""Create a "bump" commit"""
|
||||
args = [
|
||||
"--allow-empty",
|
||||
"-m",
|
||||
"Bump"
|
||||
]
|
||||
git("commit", *args)
|
||||
git("push")
|
||||
|
||||
|
||||
class Build:
|
||||
"""Wrapper class to work with module builds
|
||||
@@ -108,6 +119,7 @@ class 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):
|
||||
@@ -116,6 +128,7 @@ class Build:
|
||||
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
|
||||
@@ -135,6 +148,16 @@ class Build:
|
||||
self._build_id = int(re.search(self._mbs_api + r"module-builds/(\d+)", stdout).group(1))
|
||||
return self._build_id
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the module build
|
||||
|
||||
: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(
|
||||
"utf-8")
|
||||
return stdout
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""Module build data cache for this build fetched from MBS"""
|
||||
@@ -157,26 +180,57 @@ class Build:
|
||||
self._component_data = r.json()
|
||||
return self._component_data
|
||||
|
||||
@property
|
||||
def module_build_data(self):
|
||||
"""Verbose module build
|
||||
|
||||
:return: Dictionary of the verbose module build parameters
|
||||
:rtype: dict
|
||||
"""
|
||||
if self._build_id:
|
||||
params = {
|
||||
"verbose": True,
|
||||
}
|
||||
r = requests.get(f"{self._mbs_api}module-builds/{self._build_id}", params=params)
|
||||
r.raise_for_status()
|
||||
self._module_build_data = r.json()
|
||||
return self._module_build_data
|
||||
|
||||
@property
|
||||
def state_name(self):
|
||||
"""Name of the state of this module build"""
|
||||
return self.data["state_name"]
|
||||
|
||||
def components(self, state="COMPLETE", batch=None):
|
||||
"""Components of this module build which are in some state and in some batch
|
||||
def components(self, state=None, batch=None, package=None):
|
||||
"""Components of this module build, optionally filtered based on properties
|
||||
|
||||
:param string state: Koji build state the components should be in
|
||||
:param int batch: the number of the batch the components should be in
|
||||
:param string package: name of the component (package)
|
||||
:return: List of filtered components
|
||||
:rtype: list of strings
|
||||
:rtype: list of dict
|
||||
"""
|
||||
filtered = self.component_data["items"]
|
||||
if batch is not None:
|
||||
filtered = filter(lambda x: x["batch"] == batch, filtered)
|
||||
if state is not None:
|
||||
filtered = filter(lambda x: x["state_name"] == state, filtered)
|
||||
if package is not None:
|
||||
filtered = filter(lambda x: x["package"] == package, filtered)
|
||||
|
||||
return [item["package"] for item in filtered]
|
||||
return list(filtered)
|
||||
|
||||
def component_names(self, state=None, batch=None, package=None):
|
||||
"""Component names of this module build, optionally filtered based on properties
|
||||
|
||||
:param string state: Koji build state the components should be in
|
||||
:param int batch: the number of the batch the components should be in
|
||||
:param string: name of component (package):
|
||||
:return: List of components packages
|
||||
:rtype: list of strings
|
||||
"""
|
||||
components = self.components(state, batch, package)
|
||||
return [item["package"] for item in components]
|
||||
|
||||
def batches(self):
|
||||
"""
|
||||
@@ -195,6 +249,28 @@ class Build:
|
||||
|
||||
return batches
|
||||
|
||||
def wait_for_koji_task_id(self, package, batch, timeout=60, sleep=10):
|
||||
"""Wait until the component is submitted to Koji (has a task_id)
|
||||
|
||||
:param string: name of component (package)
|
||||
:param int batch: the number of the batch the components should be in
|
||||
:param int timeout: time in seconds
|
||||
:param int sleep: time in seconds
|
||||
"""
|
||||
start = time.time()
|
||||
while time.time() - start <= timeout:
|
||||
# Clear cached data
|
||||
self._component_data = None
|
||||
components = self.components(package=package, batch=batch)
|
||||
# Wait until the right component appears and has a task_id
|
||||
if components and components[0]["task_id"]:
|
||||
return components[0]["task_id"]
|
||||
time.sleep(sleep)
|
||||
|
||||
raise RuntimeError(
|
||||
f'Koji task for "{package}" did not start in {timeout} seconds'
|
||||
)
|
||||
|
||||
def nvr(self, name_suffix=""):
|
||||
"""NVR dictionary of this module build
|
||||
|
||||
@@ -207,3 +283,18 @@ class Build:
|
||||
"version": self.data["stream"].replace("-", "_"),
|
||||
"release": f'{self.data["version"]}.{self.data["context"]}',
|
||||
}
|
||||
|
||||
def was_cancelled(self):
|
||||
"""Checking in the status trace if module was canceled
|
||||
|
||||
:return: Whether exists required status
|
||||
:rtype: bool
|
||||
"""
|
||||
for item in self.module_build_data["state_trace"]:
|
||||
if (
|
||||
item["reason"] is not None
|
||||
and "Canceled" in item["reason"]
|
||||
and item["state_name"] == "failed"
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
2
tox.ini
2
tox.ini
@@ -79,6 +79,6 @@ deps =
|
||||
sh
|
||||
# Set this to /etc/pki/tls/certs/ca-bundle.crt, for example,
|
||||
# if the instance tested has a self-signed certificate.
|
||||
passenv = REQUESTS_CA_BUNDLE MBS_TEST_CONFIG MBS_TEST_WORKERS
|
||||
passenv = REQUESTS_CA_BUNDLE MBS_TEST_CONFIG MBS_TEST_WORKERS HOME
|
||||
commands =
|
||||
pytest -vv --confcutdir=tests/integration -n {env:MBS_TEST_WORKERS:0} {posargs:tests/integration}
|
||||
|
||||
Reference in New Issue
Block a user