Add test_submit_module_build

This commit is contained in:
jobrauer
2020-04-15 08:56:54 +02:00
parent e9df47d80c
commit 6385d465d7
5 changed files with 104 additions and 36 deletions

View File

@@ -33,7 +33,7 @@ def pkg_util(test_env):
"""Fixture to interact with the packaging utility
:return: Packaging utility configured for the tests
:rtype: object of utils.PackagingUtility
:rtype: utils.PackagingUtility
"""
return utils.PackagingUtility(test_env["packaging_utility"], test_env["mbs_api"])
@@ -99,10 +99,7 @@ def clone_and_start_build(repo, pkg_util):
builds = pkg_util.run("--optional", "rebuild_strategy=all")
yield repo, builds
for build in builds:
try:
pkg_util.cancel(build)
except sh.ErrorReturnCode:
pass # we don't need to bother with clean-up errors
pkg_util.cancel(build, ignore_errors=True)
@pytest.fixture(scope="function")

View File

@@ -136,3 +136,6 @@ testdata:
static_context:
module: testmodule
branch: test-static-context
rest_submit_module_build:
module: testmodule
branch: test-submit-module

View File

@@ -0,0 +1,48 @@
from requests import HTTPError
def assert_build_in_build_state(mbs, build):
"""Assert build state was reached and then cancel the build using REST."""
try:
mbs.wait_for_module_build(build, lambda bld: bld.get("state") == 2, timeout=10)
finally:
mbs.cancel_module_build(build.id)
def test_rest_submit_module_build(pkg_util, scenario, repo, mbs):
"""Test module build submission. Tests only whether or not
build gets accepted and transitions successfully to the build state.
Two variants:
* submit module build with modulemd yaml (test YAMLFileHandler)
* submit module build with scmurl (test SCMHandler)
..are combined into one method to reuse 1 single test branch.
Steps:
* Submit module build using module's SCM URL (HTTP POST).
* Assert that build reaches 'build' state.
* Cancel the build (HTTP PATCH)
"""
# 1) SCMURL submission
repo.bump()
scmurl = pkg_util.giturl().replace("#", "?#")
branch = scenario["branch"]
data = {"scmurl": scmurl, "branch": branch}
builds = mbs.submit_module_build(data)
assert len(builds) == 1
assert_build_in_build_state(mbs, builds[0])
# 2) YAML submission (might not be enabled, but if it is, let's test it)
repo.bump()
data = {"modulemd": str(repo.modulemd)}
try:
builds = mbs.submit_module_build(data)
except HTTPError as e:
if "YAML submission is not enabled" not in e.response.text:
raise
else:
assert_build_in_build_state(mbs, builds[0])

View File

@@ -20,6 +20,13 @@ our_sh = sh(_out=sys.stdout, _err=sys.stderr, _tee=True)
from our_sh import Command, git, pushd # noqa
def get_kerberos_auth():
"""Get the 'default' Kerberos auth header field"""
# (!) User executing this request must be allowed to do so on the target MBS instance.
# MBS server does not support mutual auth, so make it optional (inspired by mbs-cli).
return requests_kerberos.HTTPKerberosAuth(mutual_authentication=requests_kerberos.OPTIONAL)
class Koji:
"""Wrapper class to work with Koji
@@ -113,14 +120,12 @@ class Repo:
:attribute string module_name: name of the module stored in this repo
:attribute string branch: name of the branch, the repo is checked-out
:attribute string giturl: GIT URL of the repo/branch
:attribute dict _modulemd: Modulemd file as read from the repo
"""
def __init__(self, module_name, branch):
self.module_name = module_name
self.branch = branch
self._modulemd = None
self._version = None
@@ -243,18 +248,23 @@ class PackagingUtility:
return stdout
def cancel(self, build):
def cancel(self, build, ignore_errors=False):
"""Cancel the module build
:param Build build: the Build object of the module build to be cancelled.
:param bool ignore_errors: ignore when command fails (ErrorReturnCode exception)
:return: Standard output of the "module-build-cancel <build id=""> command
:rtype: str
"""
stdout = self._packaging_utility("module-build-cancel", build.id).stdout.decode(
"utf-8")
return stdout
cmd = "module-build-cancel"
try:
return self._packaging_utility(cmd, build.id).stdout.decode("utf-8")
except sh.ErrorReturnCode:
if not ignore_errors:
raise
def giturl(self):
"""Get target URL of the current repository/branch"""
return self._packaging_utility("giturl").stdout.decode("utf-8").strip()
def clone(self, *args):
@@ -519,9 +529,10 @@ class MBS:
:param str stream: Stream name
:param str order_desc_by: Optional sorting parameter e.g. "version"
:return: list of Build objects
:rtype: list
:rtype: list[Build]
"""
url = f"{self._mbs_api}module-builds/"
payload = {'name': module, "stream": stream}
if order_desc_by:
payload.update({"order_desc_by": order_desc_by})
@@ -530,40 +541,25 @@ class MBS:
return [Build(self._mbs_api, build["id"]) for build in r.json()["items"]]
def import_module(self, scmurl):
"""Import module from SCM URL (modulemd).
"""Import module from SCM URL.
:param str scmurl:
:return: requests response
:rtype: requests response object
"""
url = f"{self._mbs_api}import-module/"
headers = {"Content-Type": "application/json"}
data = json.dumps({'scmurl': scmurl})
# (!) User executing this request must be allowed to do so on the target MBS instance.
# MBS server does not support mutual auth, so make it optional (inspired by mbs-cli).
auth = requests_kerberos.HTTPKerberosAuth(mutual_authentication=requests_kerberos.OPTIONAL)
response = requests.post(
url,
auth=auth,
headers=headers,
verify=False,
data=data
)
try:
response.raise_for_status()
return response
except requests.exceptions.HTTPError:
# response message contains useful information, which requests module omits
pytest.fail(response.text)
r = requests.post(url, auth=get_kerberos_auth(), verify=False, data=data)
r.raise_for_status()
return r
def get_module_builds(self, **kwargs):
"""Query MBS API on module-builds endpoint
:attribute **kwargs: options for the HTTP GET
:return: list of Build objects
:rtype: list
:return: matched build entries
:rtype: list[Build]
:Keyword Arguments: passed directly to the request as HTTP params.
"""
url = f"{self._mbs_api}module-builds/"
r = requests.get(url, params=kwargs)
@@ -574,9 +570,10 @@ class MBS:
def get_module_build(self, build_data, **kwargs):
"""Query MBS API on module-builds endpoint for a specific build
:attribute build_data (int|Build): build ID
:param int|Build build_data: build ID
:return: module build object
:rtype: Build
:Keyword Arguments: passed directly to the request as HTTP params.
"""
build_id = self._get_build_id(build_data)
url = f"{self._mbs_api}module-builds/{build_id}"
@@ -585,6 +582,29 @@ class MBS:
r.raise_for_status()
return Build(self._mbs_api, r.json()["id"])
def submit_module_build(self, data):
"""Submit a module build with arbitrary payload.
:param dict data: payload of the POST request
1) SCMURL submission: data = {scmurl, branch}
2) YAML submission: data = {modulemd: <str(modulemd as dict)>}
:return: submitted build(s)
:rtype: list[Build]
"""
url = f"{self._mbs_api}module-builds/"
r = requests.post(url, verify=False, auth=get_kerberos_auth(), data=json.dumps(data))
r.raise_for_status()
return [Build(self._mbs_api, build["id"]) for build in r.json()]
def cancel_module_build(self, build_id):
"""PATCH the state field of a module build to cancel it"""
url = f"{self._mbs_api}module-builds/{build_id}"
data = json.dumps({"state": "failed"})
response = requests.patch(url, auth=get_kerberos_auth(), verify=False, data=data)
response.raise_for_status()
def wait_for_module_build(self, build_data, predicate_func, timeout=60, interval=5):
"""Wait for module build. Wait until the specified function returns True.