mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-04-05 11:48:33 +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.
|
||||
#
|
||||
# Written by Petr Šabata <contyk@redhat.com>
|
||||
# Matt Prahl <mprahl@redhat.com>
|
||||
|
||||
"""The module build orchestrator for Modularity.
|
||||
|
||||
@@ -45,6 +46,8 @@ from flask_sqlalchemy import SQLAlchemy
|
||||
from os import sys
|
||||
import rida.logger
|
||||
from logging import getLogger
|
||||
from rida.errors import (ValidationError, Unauthorized, UnprocessableEntity,
|
||||
Conflict, NotFound, Forbidden, json_error)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar("RIDA_SETTINGS", silent=True)
|
||||
@@ -58,6 +61,47 @@ else:
|
||||
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
|
||||
conf = rida.config.from_app_config()
|
||||
rida.logger.init_logging(conf)
|
||||
|
||||
@@ -20,6 +20,37 @@
|
||||
#
|
||||
# Written by Matt Prahl <mprahl@redhat.com>
|
||||
""" Defines custom exceptions and error handling functions """
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
class ValidationError(ValueError):
|
||||
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
|
||||
|
||||
from rida import log
|
||||
from rida.errors import Unauthorized, ValidationError
|
||||
import rida.utils
|
||||
|
||||
|
||||
@@ -62,7 +63,7 @@ class SCM(object):
|
||||
|
||||
:param str url: The unmodified scmurl
|
||||
:param list allowed_scm: The list of allowed SCMs, optional
|
||||
:raises: RuntimeError
|
||||
:raises: Unauthorized or ValidationError
|
||||
"""
|
||||
|
||||
if allowed_scm:
|
||||
@@ -70,7 +71,8 @@ class SCM(object):
|
||||
if url.startswith(allowed):
|
||||
break
|
||||
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
|
||||
|
||||
@@ -79,7 +81,7 @@ class SCM(object):
|
||||
self.scheme = scmtype
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('Invalid SCM URL: %s' % url)
|
||||
raise ValidationError('Invalid SCM URL: %s' % url)
|
||||
|
||||
if self.scheme == "git":
|
||||
match = re.search(r"^(?P<repository>.*/(?P<name>[^?]*))(\?#(?P<commit>.*))?", url)
|
||||
@@ -89,7 +91,7 @@ class SCM(object):
|
||||
self.name = self.name[:-4]
|
||||
self.commit = match.group("commit")
|
||||
else:
|
||||
raise RuntimeError("Unhandled SCM scheme: %s" % self.scheme)
|
||||
raise ValidationError("Unhandled SCM scheme: %s" % self.scheme)
|
||||
|
||||
@staticmethod
|
||||
@rida.utils.retry(wait_on=RuntimeError)
|
||||
|
||||
@@ -40,7 +40,8 @@ import tempfile
|
||||
from rida import app, conf, db, log
|
||||
from rida import models
|
||||
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"])
|
||||
@@ -48,15 +49,16 @@ def submit_build():
|
||||
"""Handles new module build submissions."""
|
||||
username = rida.auth.is_packager(conf.pkgdb_api_url)
|
||||
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:
|
||||
r = json.loads(request.get_data().decode("utf-8"))
|
||||
except:
|
||||
return "Invalid JSON submitted", 400
|
||||
raise ValidationError('Invalid JSON submitted')
|
||||
|
||||
if "scmurl" not in r:
|
||||
return "Missing scmurl", 400
|
||||
raise ValidationError('Missing scmurl')
|
||||
|
||||
url = r["scmurl"]
|
||||
urlallowed = False
|
||||
@@ -68,7 +70,7 @@ def submit_build():
|
||||
break
|
||||
|
||||
if not urlallowed:
|
||||
return "The submitted scmurl isn't allowed", 403
|
||||
raise Unauthorized('The submitted scmurl isn\'t allowed')
|
||||
|
||||
yaml = str()
|
||||
td = None
|
||||
@@ -80,14 +82,6 @@ def submit_build():
|
||||
|
||||
with open(cofn, "r") as mmdfile:
|
||||
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:
|
||||
try:
|
||||
if td is not None:
|
||||
@@ -101,10 +95,10 @@ def submit_build():
|
||||
try:
|
||||
mmd.loads(yaml)
|
||||
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():
|
||||
return "Module already exists", 409
|
||||
raise Conflict('Module already exists')
|
||||
|
||||
module = models.ModuleBuild.create(
|
||||
db.session,
|
||||
@@ -117,33 +111,34 @@ def submit_build():
|
||||
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():
|
||||
if pkg.get("repository") and not conf.rpms_allow_repository:
|
||||
return failure("Custom component repositories aren't allowed", 403)
|
||||
if pkg.get("cache") and not conf.rpms_allow_cache:
|
||||
return failure("Custom component caches aren't allowed", 403)
|
||||
if not pkg.get("repository"):
|
||||
pkg["repository"] = conf.rpms_default_repository + pkgname
|
||||
if not pkg.get("cache"):
|
||||
pkg["cache"] = conf.rpms_default_cache + pkgname
|
||||
if not pkg.get("commit"):
|
||||
try:
|
||||
pkg["commit"] = rida.scm.SCM(pkg["repository"]).get_latest()
|
||||
except Exception as e:
|
||||
return failure("Failed to get the latest commit: %s" % pkgname, 422)
|
||||
try:
|
||||
if pkg.get("repository") and not conf.rpms_allow_repository:
|
||||
raise Unauthorized(
|
||||
"Custom component repositories aren't allowed")
|
||||
if pkg.get("cache") and not conf.rpms_allow_cache:
|
||||
raise Unauthorized("Custom component caches aren't allowed")
|
||||
if not pkg.get("repository"):
|
||||
pkg["repository"] = conf.rpms_default_repository + pkgname
|
||||
if not pkg.get("cache"):
|
||||
pkg["cache"] = conf.rpms_default_cache + pkgname
|
||||
if not pkg.get("commit"):
|
||||
try:
|
||||
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"]
|
||||
|
||||
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(
|
||||
module_id=module.id,
|
||||
@@ -165,10 +160,7 @@ def submit_build():
|
||||
@app.route("/rida/module-builds/", methods=["GET"])
|
||||
def query_builds():
|
||||
"""Lists all tracked module builds."""
|
||||
try:
|
||||
p_query = filter_module_builds(request)
|
||||
except ValidationError as e:
|
||||
return e.message, 400
|
||||
p_query = filter_module_builds(request)
|
||||
|
||||
json_data = {
|
||||
'meta': pagination_metadata(p_query)
|
||||
@@ -192,4 +184,4 @@ def query_build(id):
|
||||
if module:
|
||||
return jsonify(module.api_json()), 200
|
||||
else:
|
||||
return "No such module found.", 404
|
||||
raise NotFound('No such module found.')
|
||||
|
||||
Reference in New Issue
Block a user