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:
Merlin Mathesius
2019-03-12 15:57:12 -05:00
committed by mprahl
parent 64053e7fed
commit a46a4ec470
2 changed files with 72 additions and 43 deletions

View File

@@ -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')

View File

@@ -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