mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-05-16 13:56:11 +08:00
Convert errors to JSON
Signed-off-by: Matt Prahl <mprahl@redhat.com> Signed-off-by: Nils Philippsen <nils@redhat.com>
This commit is contained in:
committed by
Nils Philippsen
parent
d809d34ed7
commit
fbe1beee54
@@ -22,6 +22,7 @@
|
|||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
#
|
#
|
||||||
# Written by Petr Šabata <contyk@redhat.com>
|
# Written by Petr Šabata <contyk@redhat.com>
|
||||||
|
# Matt Prahl <mprahl@redhat.com>
|
||||||
|
|
||||||
"""The module build orchestrator for Modularity.
|
"""The module build orchestrator for Modularity.
|
||||||
|
|
||||||
@@ -45,6 +46,8 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
from os import sys
|
from os import sys
|
||||||
import rida.logger
|
import rida.logger
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from rida.errors import (ValidationError, Unauthorized, UnprocessableEntity,
|
||||||
|
Conflict, NotFound, Forbidden, json_error)
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_envvar("RIDA_SETTINGS", silent=True)
|
app.config.from_envvar("RIDA_SETTINGS", silent=True)
|
||||||
@@ -58,6 +61,47 @@ else:
|
|||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(ValidationError)
|
||||||
|
def validationerror_error(e):
|
||||||
|
"""Flask error handler for ValidationError exceptions"""
|
||||||
|
return json_error(400, 'Bad Request', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(Unauthorized)
|
||||||
|
def unauthorized_error(e):
|
||||||
|
"""Flask error handler for NotAuthorized exceptions"""
|
||||||
|
return json_error(401, 'Unauthorized', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(Forbidden)
|
||||||
|
def forbidden_error(e):
|
||||||
|
"""Flask error handler for Forbidden exceptions"""
|
||||||
|
return json_error(403, 'Forbidden', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(RuntimeError)
|
||||||
|
def runtimeerror_error(e):
|
||||||
|
"""Flask error handler for RuntimeError exceptions"""
|
||||||
|
return json_error(500, 'Internal Server Error', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(UnprocessableEntity)
|
||||||
|
def unprocessableentity_error(e):
|
||||||
|
"""Flask error handler for UnprocessableEntity exceptions"""
|
||||||
|
return json_error(422, 'Unprocessable Entity', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(Conflict)
|
||||||
|
def conflict_error(e):
|
||||||
|
"""Flask error handler for Conflict exceptions"""
|
||||||
|
return json_error(409, 'Conflict', e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(NotFound)
|
||||||
|
def notfound_error(e):
|
||||||
|
"""Flask error handler for Conflict exceptions"""
|
||||||
|
return json_error(404, 'Not Found', e.args[0])
|
||||||
|
|
||||||
import rida.config
|
import rida.config
|
||||||
conf = rida.config.from_app_config()
|
conf = rida.config.from_app_config()
|
||||||
rida.logger.init_logging(conf)
|
rida.logger.init_logging(conf)
|
||||||
|
|||||||
@@ -20,6 +20,37 @@
|
|||||||
#
|
#
|
||||||
# Written by Matt Prahl <mprahl@redhat.com>
|
# Written by Matt Prahl <mprahl@redhat.com>
|
||||||
""" Defines custom exceptions and error handling functions """
|
""" Defines custom exceptions and error handling functions """
|
||||||
|
from flask import jsonify
|
||||||
|
|
||||||
|
|
||||||
class ValidationError(ValueError):
|
class ValidationError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Unauthorized(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Forbidden(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnprocessableEntity(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Conflict(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotFound(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def json_error(status, error, message):
|
||||||
|
response = jsonify(
|
||||||
|
{'status': status,
|
||||||
|
'error': error,
|
||||||
|
'message': message})
|
||||||
|
response.status_code = status
|
||||||
|
return response
|
||||||
|
|||||||
10
rida/scm.py
10
rida/scm.py
@@ -36,6 +36,7 @@ import tempfile
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from rida import log
|
from rida import log
|
||||||
|
from rida.errors import Unauthorized, ValidationError
|
||||||
import rida.utils
|
import rida.utils
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ class SCM(object):
|
|||||||
|
|
||||||
:param str url: The unmodified scmurl
|
:param str url: The unmodified scmurl
|
||||||
:param list allowed_scm: The list of allowed SCMs, optional
|
:param list allowed_scm: The list of allowed SCMs, optional
|
||||||
:raises: RuntimeError
|
:raises: Unauthorized or ValidationError
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if allowed_scm:
|
if allowed_scm:
|
||||||
@@ -70,7 +71,8 @@ class SCM(object):
|
|||||||
if url.startswith(allowed):
|
if url.startswith(allowed):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('%s is not in the list of allowed SCMs' % url)
|
raise Unauthorized(
|
||||||
|
'%s is not in the list of allowed SCMs' % url)
|
||||||
|
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ class SCM(object):
|
|||||||
self.scheme = scmtype
|
self.scheme = scmtype
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Invalid SCM URL: %s' % url)
|
raise ValidationError('Invalid SCM URL: %s' % url)
|
||||||
|
|
||||||
if self.scheme == "git":
|
if self.scheme == "git":
|
||||||
match = re.search(r"^(?P<repository>.*/(?P<name>[^?]*))(\?#(?P<commit>.*))?", url)
|
match = re.search(r"^(?P<repository>.*/(?P<name>[^?]*))(\?#(?P<commit>.*))?", url)
|
||||||
@@ -89,7 +91,7 @@ class SCM(object):
|
|||||||
self.name = self.name[:-4]
|
self.name = self.name[:-4]
|
||||||
self.commit = match.group("commit")
|
self.commit = match.group("commit")
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unhandled SCM scheme: %s" % self.scheme)
|
raise ValidationError("Unhandled SCM scheme: %s" % self.scheme)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@rida.utils.retry(wait_on=RuntimeError)
|
@rida.utils.retry(wait_on=RuntimeError)
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ import tempfile
|
|||||||
from rida import app, conf, db, log
|
from rida import app, conf, db, log
|
||||||
from rida import models
|
from rida import models
|
||||||
from rida.utils import pagination_metadata, filter_module_builds
|
from rida.utils import pagination_metadata, filter_module_builds
|
||||||
from errors import ValidationError
|
from errors import (ValidationError, Unauthorized, UnprocessableEntity,
|
||||||
|
Conflict, NotFound)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/rida/module-builds/", methods=["POST"])
|
@app.route("/rida/module-builds/", methods=["POST"])
|
||||||
@@ -48,15 +49,16 @@ def submit_build():
|
|||||||
"""Handles new module build submissions."""
|
"""Handles new module build submissions."""
|
||||||
username = rida.auth.is_packager(conf.pkgdb_api_url)
|
username = rida.auth.is_packager(conf.pkgdb_api_url)
|
||||||
if not username:
|
if not username:
|
||||||
return "You must use your Fedora certificate when submitting a new build", 403
|
raise Unauthorized("You must use your Fedora certificate "
|
||||||
|
"when submitting a new build")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = json.loads(request.get_data().decode("utf-8"))
|
r = json.loads(request.get_data().decode("utf-8"))
|
||||||
except:
|
except:
|
||||||
return "Invalid JSON submitted", 400
|
raise ValidationError('Invalid JSON submitted')
|
||||||
|
|
||||||
if "scmurl" not in r:
|
if "scmurl" not in r:
|
||||||
return "Missing scmurl", 400
|
raise ValidationError('Missing scmurl')
|
||||||
|
|
||||||
url = r["scmurl"]
|
url = r["scmurl"]
|
||||||
urlallowed = False
|
urlallowed = False
|
||||||
@@ -68,7 +70,7 @@ def submit_build():
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not urlallowed:
|
if not urlallowed:
|
||||||
return "The submitted scmurl isn't allowed", 403
|
raise Unauthorized('The submitted scmurl isn\'t allowed')
|
||||||
|
|
||||||
yaml = str()
|
yaml = str()
|
||||||
td = None
|
td = None
|
||||||
@@ -80,14 +82,6 @@ def submit_build():
|
|||||||
|
|
||||||
with open(cofn, "r") as mmdfile:
|
with open(cofn, "r") as mmdfile:
|
||||||
yaml = mmdfile.read()
|
yaml = mmdfile.read()
|
||||||
except Exception as e:
|
|
||||||
if "is not in the list of allowed SCMs" in str(e):
|
|
||||||
rc = 403
|
|
||||||
elif "Invalid SCM URL" in str(e):
|
|
||||||
rc = 400
|
|
||||||
else:
|
|
||||||
rc = 500
|
|
||||||
return str(e), rc
|
|
||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
if td is not None:
|
if td is not None:
|
||||||
@@ -101,10 +95,10 @@ def submit_build():
|
|||||||
try:
|
try:
|
||||||
mmd.loads(yaml)
|
mmd.loads(yaml)
|
||||||
except:
|
except:
|
||||||
return "Invalid modulemd", 422
|
raise UnprocessableEntity('Invalid modulemd')
|
||||||
|
|
||||||
if models.ModuleBuild.query.filter_by(name=mmd.name, version=mmd.version, release=mmd.release).first():
|
if models.ModuleBuild.query.filter_by(name=mmd.name, version=mmd.version, release=mmd.release).first():
|
||||||
return "Module already exists", 409
|
raise Conflict('Module already exists')
|
||||||
|
|
||||||
module = models.ModuleBuild.create(
|
module = models.ModuleBuild.create(
|
||||||
db.session,
|
db.session,
|
||||||
@@ -117,33 +111,34 @@ def submit_build():
|
|||||||
username=username
|
username=username
|
||||||
)
|
)
|
||||||
|
|
||||||
def failure(message, code):
|
|
||||||
# TODO, we should make some note of why it failed in the db..
|
|
||||||
log.exception(message)
|
|
||||||
module.transition(conf, models.BUILD_STATES["failed"])
|
|
||||||
db.session.add(module)
|
|
||||||
db.session.commit()
|
|
||||||
return message, code
|
|
||||||
|
|
||||||
for pkgname, pkg in mmd.components.rpms.packages.items():
|
for pkgname, pkg in mmd.components.rpms.packages.items():
|
||||||
if pkg.get("repository") and not conf.rpms_allow_repository:
|
try:
|
||||||
return failure("Custom component repositories aren't allowed", 403)
|
if pkg.get("repository") and not conf.rpms_allow_repository:
|
||||||
if pkg.get("cache") and not conf.rpms_allow_cache:
|
raise Unauthorized(
|
||||||
return failure("Custom component caches aren't allowed", 403)
|
"Custom component repositories aren't allowed")
|
||||||
if not pkg.get("repository"):
|
if pkg.get("cache") and not conf.rpms_allow_cache:
|
||||||
pkg["repository"] = conf.rpms_default_repository + pkgname
|
raise Unauthorized("Custom component caches aren't allowed")
|
||||||
if not pkg.get("cache"):
|
if not pkg.get("repository"):
|
||||||
pkg["cache"] = conf.rpms_default_cache + pkgname
|
pkg["repository"] = conf.rpms_default_repository + pkgname
|
||||||
if not pkg.get("commit"):
|
if not pkg.get("cache"):
|
||||||
try:
|
pkg["cache"] = conf.rpms_default_cache + pkgname
|
||||||
pkg["commit"] = rida.scm.SCM(pkg["repository"]).get_latest()
|
if not pkg.get("commit"):
|
||||||
except Exception as e:
|
try:
|
||||||
return failure("Failed to get the latest commit: %s" % pkgname, 422)
|
pkg["commit"] = rida.scm.SCM(
|
||||||
|
pkg["repository"]).get_latest()
|
||||||
|
except Exception as e:
|
||||||
|
raise UnprocessableEntity(
|
||||||
|
"Failed to get the latest commit: %s" % pkgname)
|
||||||
|
except Exception:
|
||||||
|
module.transition(conf, models.BUILD_STATES["failed"])
|
||||||
|
db.session.add(module)
|
||||||
|
db.session.commit()
|
||||||
|
raise
|
||||||
|
|
||||||
full_url = pkg["repository"] + "?#" + pkg["commit"]
|
full_url = pkg["repository"] + "?#" + pkg["commit"]
|
||||||
|
|
||||||
if not rida.scm.SCM(full_url).is_available():
|
if not rida.scm.SCM(full_url).is_available():
|
||||||
return failure("Cannot checkout %s" % pkgname, 422)
|
raise UnprocessableEntity("Cannot checkout %s" % pkgname)
|
||||||
|
|
||||||
build = models.ComponentBuild(
|
build = models.ComponentBuild(
|
||||||
module_id=module.id,
|
module_id=module.id,
|
||||||
@@ -165,10 +160,7 @@ def submit_build():
|
|||||||
@app.route("/rida/module-builds/", methods=["GET"])
|
@app.route("/rida/module-builds/", methods=["GET"])
|
||||||
def query_builds():
|
def query_builds():
|
||||||
"""Lists all tracked module builds."""
|
"""Lists all tracked module builds."""
|
||||||
try:
|
p_query = filter_module_builds(request)
|
||||||
p_query = filter_module_builds(request)
|
|
||||||
except ValidationError as e:
|
|
||||||
return e.message, 400
|
|
||||||
|
|
||||||
json_data = {
|
json_data = {
|
||||||
'meta': pagination_metadata(p_query)
|
'meta': pagination_metadata(p_query)
|
||||||
@@ -192,4 +184,4 @@ def query_build(id):
|
|||||||
if module:
|
if module:
|
||||||
return jsonify(module.api_json()), 200
|
return jsonify(module.api_json()), 200
|
||||||
else:
|
else:
|
||||||
return "No such module found.", 404
|
raise NotFound('No such module found.')
|
||||||
|
|||||||
Reference in New Issue
Block a user