mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-04-09 21:59:58 +08:00
Accept modulemd for scratch module builds as a parameter in the submitted JSON.
Signed-off-by: Merlin Mathesius <mmathesi@redhat.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import module_build_service.auth
|
||||
from flask import request, url_for
|
||||
from flask.views import MethodView
|
||||
from six import string_types
|
||||
from io import BytesIO
|
||||
|
||||
from module_build_service import app, conf, log, models, db, version, api_version as max_api_version
|
||||
from module_build_service.utils import (
|
||||
@@ -172,10 +173,11 @@ class ModuleBuildAPI(AbstractQueryableBuildAPI):
|
||||
# Additional POST and DELETE handlers for modules follow.
|
||||
@validate_api_version()
|
||||
def post(self, api_version):
|
||||
if hasattr(request, 'files') and "yaml" in request.files:
|
||||
handler = YAMLFileHandler(request)
|
||||
data = _dict_from_request(request)
|
||||
if "modulemd" in data or (hasattr(request, "files") and "yaml" in request.files):
|
||||
handler = YAMLFileHandler(request, data)
|
||||
else:
|
||||
handler = SCMHandler(request)
|
||||
handler = SCMHandler(request, data)
|
||||
|
||||
if conf.no_auth is True and handler.username == "anonymous" and "owner" in handler.data:
|
||||
handler.username = handler.data["owner"]
|
||||
@@ -312,16 +314,9 @@ class ImportModuleAPI(MethodView):
|
||||
|
||||
|
||||
class BaseHandler(object):
|
||||
def __init__(self, request):
|
||||
def __init__(self, request, data=None):
|
||||
self.username, self.groups = module_build_service.auth.get_user(request)
|
||||
if "multipart/form-data" in request.headers.get("Content-Type", ""):
|
||||
self.data = request.form.to_dict()
|
||||
else:
|
||||
try:
|
||||
self.data = json.loads(request.get_data().decode("utf-8"))
|
||||
except Exception:
|
||||
log.error('Invalid JSON submitted')
|
||||
raise ValidationError('Invalid JSON submitted')
|
||||
self.data = data or _dict_from_request(request)
|
||||
|
||||
# canonicalize and validate scratch option
|
||||
if 'scratch' in self.data and str_to_bool(str(self.data['scratch'])):
|
||||
@@ -342,7 +337,8 @@ class BaseHandler(object):
|
||||
|
||||
@property
|
||||
def optional_params(self):
|
||||
return {k: v for k, v in self.data.items() if k not in ["owner", "scmurl", "branch"]}
|
||||
return {k: v for k, v in self.data.items() if k not in
|
||||
["owner", "scmurl", "branch", "modulemd", "module_name"]}
|
||||
|
||||
def _validate_dep_overrides_format(self, key):
|
||||
"""
|
||||
@@ -367,7 +363,13 @@ class BaseHandler(object):
|
||||
def validate_optional_params(self):
|
||||
module_build_columns = set([col.key for col in models.ModuleBuild.__table__.columns])
|
||||
other_params = set([
|
||||
'branch', 'rebuild_strategy', 'buildrequire_overrides', 'require_overrides'])
|
||||
'branch',
|
||||
'buildrequire_overrides',
|
||||
'modulemd',
|
||||
'module_name',
|
||||
'rebuild_strategy',
|
||||
'require_overrides',
|
||||
])
|
||||
valid_params = other_params | module_build_columns
|
||||
|
||||
forbidden_params = [k for k in self.data if k not in valid_params]
|
||||
@@ -427,23 +429,43 @@ class SCMHandler(BaseHandler):
|
||||
|
||||
|
||||
class YAMLFileHandler(BaseHandler):
|
||||
def __init__(self, request):
|
||||
super(YAMLFileHandler, self).__init__(request)
|
||||
def __init__(self, request, data=None):
|
||||
super(YAMLFileHandler, self).__init__(request, data)
|
||||
if not self.data['scratch'] and not conf.yaml_submit_allowed:
|
||||
raise Forbidden("YAML submission is not enabled")
|
||||
|
||||
def validate(self):
|
||||
if "yaml" not in request.files:
|
||||
if ("modulemd" not in self.data and
|
||||
(not hasattr(request, "files") or "yaml" not in request.files)):
|
||||
log.error('Invalid file submitted')
|
||||
raise ValidationError('Invalid file submitted')
|
||||
self.validate_optional_params()
|
||||
|
||||
def post(self):
|
||||
handle = request.files["yaml"]
|
||||
if "modulemd" in self.data:
|
||||
handle = BytesIO(self.data["modulemd"].encode("utf-8"))
|
||||
if "module_name" in self.data and self.data["module_name"]:
|
||||
handle.filename = self.data["module_name"]
|
||||
else:
|
||||
handle.filename = "unnamed"
|
||||
else:
|
||||
handle = request.files["yaml"]
|
||||
return submit_module_build_from_yaml(self.username, handle,
|
||||
optional_params=self.optional_params)
|
||||
|
||||
|
||||
def _dict_from_request(request):
|
||||
if "multipart/form-data" in request.headers.get("Content-Type", ""):
|
||||
data = request.form.to_dict()
|
||||
else:
|
||||
try:
|
||||
data = json.loads(request.get_data().decode("utf-8"))
|
||||
except Exception:
|
||||
log.error('Invalid JSON submitted')
|
||||
raise ValidationError('Invalid JSON submitted')
|
||||
return data
|
||||
|
||||
|
||||
def register_api():
|
||||
""" Registers the MBS API. """
|
||||
module_view = ModuleBuildAPI.as_view('module_builds')
|
||||
|
||||
@@ -26,10 +26,9 @@ from datetime import datetime
|
||||
import module_build_service.scm
|
||||
|
||||
from mock import patch, PropertyMock
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from shutil import copyfile
|
||||
from os import path, mkdir
|
||||
from os.path import dirname
|
||||
from os.path import basename, dirname, splitext
|
||||
from requests.utils import quote
|
||||
import hashlib
|
||||
import pytest
|
||||
@@ -1703,8 +1702,8 @@ class TestViews:
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch('module_build_service.config.Config.modules_allow_scratch',
|
||||
new_callable=PropertyMock, return_value=True)
|
||||
def test_submit_scratch_build(self, mocked_allow_scratch, mocked_scm, mocked_get_user,
|
||||
api_version):
|
||||
def test_submit_scratch_build(
|
||||
self, mocked_allow_scratch, mocked_scm, mocked_get_user, api_version):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -1760,9 +1759,8 @@ class TestViews:
|
||||
@patch('module_build_service.scm.SCM')
|
||||
@patch('module_build_service.config.Config.modules_allow_scratch',
|
||||
new_callable=PropertyMock, return_value=False)
|
||||
def test_submit_scratch_build_not_allowed(self, mocked_allow_scratch,
|
||||
mocked_scm, mocked_get_user,
|
||||
api_version):
|
||||
def test_submit_scratch_build_not_allowed(
|
||||
self, mocked_allow_scratch, mocked_scm, mocked_get_user, api_version):
|
||||
FakeSCM(mocked_scm, 'testmodule', 'testmodule.yaml',
|
||||
'620ec77321b2ea7b0d67d82992dda3e1d67055b4')
|
||||
|
||||
@@ -1789,21 +1787,21 @@ class TestViews:
|
||||
new_callable=PropertyMock, return_value=True)
|
||||
@patch('module_build_service.config.Config.yaml_submit_allowed',
|
||||
new_callable=PropertyMock, return_value=True)
|
||||
def test_submit_scratch_build_with_mmd(self, mocked_allow_yaml,
|
||||
mocked_allow_scratch,
|
||||
mocked_get_user,
|
||||
api_version):
|
||||
def test_submit_scratch_build_with_mmd(
|
||||
self, mocked_allow_yaml, mocked_allow_scratch, mocked_get_user, api_version):
|
||||
base_dir = path.abspath(path.dirname(__file__))
|
||||
mmd_path = path.join(base_dir, '..', 'staged_data', 'testmodule.yaml')
|
||||
post_url = '/module-build-service/{0}/module-builds/'.format(api_version)
|
||||
with open(mmd_path, 'rb') as f:
|
||||
yaml_file = FileStorage(f)
|
||||
post_data = {
|
||||
'branch': 'master',
|
||||
'scratch': True,
|
||||
'yaml': yaml_file,
|
||||
}
|
||||
rv = self.client.post(post_url, content_type='multipart/form-data', data=post_data)
|
||||
modulemd = f.read().decode('utf-8')
|
||||
|
||||
post_data = {
|
||||
'branch': 'master',
|
||||
'scratch': True,
|
||||
'modulemd': modulemd,
|
||||
'module_name': str(splitext(basename(mmd_path))[0]),
|
||||
}
|
||||
rv = self.client.post(post_url, data=json.dumps(post_data))
|
||||
data = json.loads(rv.data)
|
||||
|
||||
if api_version >= 2:
|
||||
@@ -1857,13 +1855,22 @@ class TestViews:
|
||||
mmd_path = path.join(base_dir, '..', 'staged_data', 'testmodule.yaml')
|
||||
post_url = '/module-build-service/{0}/module-builds/'.format(api_version)
|
||||
with open(mmd_path, 'rb') as f:
|
||||
yaml_file = FileStorage(f)
|
||||
post_data = {
|
||||
'branch': 'master',
|
||||
'scratch': True,
|
||||
'yaml': yaml_file,
|
||||
}
|
||||
rv = self.client.post(post_url, content_type='multipart/form-data', data=post_data)
|
||||
modulemd = f.read().decode('utf-8')
|
||||
|
||||
post_data = {
|
||||
'branch': 'master',
|
||||
'scratch': True,
|
||||
'modulemd': modulemd,
|
||||
'module_name': str(splitext(basename(mmd_path))[0]),
|
||||
}
|
||||
rv = self.client.post(post_url, data=json.dumps(post_data))
|
||||
data = json.loads(rv.data)
|
||||
|
||||
if api_version >= 2:
|
||||
assert isinstance(data, list)
|
||||
assert len(data) == 1
|
||||
data = data[0]
|
||||
|
||||
# this test is the same as the previous except YAML_SUBMIT_ALLOWED is False,
|
||||
# but it should still succeed since yaml is always allowed for scratch builds
|
||||
assert rv.status_code == 201
|
||||
|
||||
Reference in New Issue
Block a user