Add error IDs for when an unknown blueprint is requested.

This adds some fairly redundant code to the beginning of all the
blueprint routes to attempt reading a commit from git for the
blueprint's recipe.  If it succeeds, the blueprint exists and the route
can continue.  Otherwise, return an error.  Hopefully this doesn't slow
things down too much.

(cherry picked from commit a925cc7ddb)
This commit is contained in:
Chris Lumens 2018-08-08 15:41:40 -04:00
parent a2ce0686ca
commit 2f8f076b1f
3 changed files with 33 additions and 12 deletions

View File

@ -25,5 +25,12 @@ BAD_LIMIT_OR_OFFSET = "BadLimitOrOffset"
# a build that is not yet done. # a build that is not yet done.
BUILD_IN_WRONG_STATE = "BuildInWrongState" BUILD_IN_WRONG_STATE = "BuildInWrongState"
# Returned from the API when a blueprint name or other similar identifier is
# given that contains invalid characters.
INVALID_NAME = "InvalidName"
# Returned from the API when a blueprint that was requested does not exist.
UNKNOWN_BLUEPRINT = "UnknownBlueprint"
# Returned from the API when a UUID that was requested does not exist. # Returned from the API when a UUID that was requested does not exist.
UNKNOWN_UUID = "UnknownUUID" UNKNOWN_UUID = "UnknownUUID"

View File

@ -989,7 +989,7 @@ from pylorax.api.projects import modules_list, modules_info, ProjectsError, repo
from pylorax.api.projects import get_repo_sources, delete_repo_source, source_to_repo, dnf_repo_to_file_repo from pylorax.api.projects import get_repo_sources, delete_repo_source, source_to_repo, dnf_repo_to_file_repo
from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info
from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log from pylorax.api.queue import uuid_tar, uuid_image, uuid_cancel, uuid_log
from pylorax.api.recipes import list_branch_files, read_recipe_commit, recipe_filename, list_commits from pylorax.api.recipes import RecipeError, list_branch_files, read_recipe_commit, recipe_filename, list_commits
from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe
from pylorax.api.recipes import tag_recipe_commit, recipe_diff from pylorax.api.recipes import tag_recipe_commit, recipe_diff
from pylorax.api.regexes import VALID_API_STRING from pylorax.api.regexes import VALID_API_STRING
@ -1011,6 +1011,15 @@ def take_limits(iterable, offset, limit):
""" """
return iterable[offset:][:limit] return iterable[offset:][:limit]
def blueprint_exists(api, branch, blueprint_name):
try:
with api.config["GITLOCK"].lock:
read_recipe_commit(api.config["GITLOCK"].repo, branch, blueprint_name)
return True
except RecipeError:
return False
def v0_api(api): def v0_api(api):
# Note that Sphinx will not generate documentations for any of these. # Note that Sphinx will not generate documentations for any of these.
@api.route("/api/v0/blueprints/list") @api.route("/api/v0/blueprints/list")
@ -1073,7 +1082,7 @@ def v0_api(api):
if not ws_blueprint and not git_blueprint: if not ws_blueprint and not git_blueprint:
# Neither blueprint, return an error # Neither blueprint, return an error
errors.append("%s: %s" % (blueprint_name, ", ".join(exceptions))) errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: %s" % (blueprint_name, ", ".join(exceptions))})
elif ws_blueprint and not git_blueprint: elif ws_blueprint and not git_blueprint:
# No git blueprint, return the workspace blueprint # No git blueprint, return the workspace blueprint
changes.append({"name":blueprint_name, "changed":True}) changes.append({"name":blueprint_name, "changed":True})
@ -1120,6 +1129,11 @@ def v0_api(api):
errors = [] errors = []
for blueprint_name in [n.strip() for n in blueprint_names.split(",")]: for blueprint_name in [n.strip() for n in blueprint_names.split(",")]:
filename = recipe_filename(blueprint_name) filename = recipe_filename(blueprint_name)
if not blueprint_exists(api, branch, blueprint_name):
errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "Unknown blueprint name: %s" % blueprint_name})
continue
try: try:
with api.config["GITLOCK"].lock: with api.config["GITLOCK"].lock:
commits = take_limits(list_commits(api.config["GITLOCK"].repo, branch, filename), offset, limit) commits = take_limits(list_commits(api.config["GITLOCK"].repo, branch, filename), offset, limit)
@ -1173,7 +1187,7 @@ def v0_api(api):
branch = request.args.get("branch", "master") branch = request.args.get("branch", "master")
if VALID_API_STRING.match(branch) is None: if VALID_API_STRING.match(branch) is None:
return jsonify(status=False, errors=["Invalid characters in branch argument"]), 400 return jsonify(status=False, errors=[{"id": INVALID_NAME, "msg": "Invalid characters in branch argument"}]), 400
try: try:
with api.config["GITLOCK"].lock: with api.config["GITLOCK"].lock:
@ -1220,14 +1234,14 @@ def v0_api(api):
branch = request.args.get("branch", "master") branch = request.args.get("branch", "master")
if VALID_API_STRING.match(branch) is None: if VALID_API_STRING.match(branch) is None:
return jsonify(status=False, errors=["Invalid characters in branch argument"]), 400 return jsonify(status=False, errors=[{"id": INVALID_NAME, "msg": "Invalid characters in branch argument"}]), 400
try: try:
with api.config["GITLOCK"].lock: with api.config["GITLOCK"].lock:
workspace_delete(api.config["GITLOCK"].repo, branch, blueprint_name) workspace_delete(api.config["GITLOCK"].repo, branch, blueprint_name)
except Exception as e: except Exception as e:
log.error("(v0_blueprints_delete_workspace) %s", str(e)) log.error("(v0_blueprints_delete_workspace) %s", str(e))
return jsonify(status=False, error=[str(e)]), 400 return jsonify(status=False, errors=[str(e)]), 400
else: else:
return jsonify(status=True) return jsonify(status=True)
@ -1244,7 +1258,7 @@ def v0_api(api):
branch = request.args.get("branch", "master") branch = request.args.get("branch", "master")
if VALID_API_STRING.match(branch) is None: if VALID_API_STRING.match(branch) is None:
return jsonify(status=False, errors=["Invalid characters in branch argument"]), 400 return jsonify(status=False, errors=[{"id": INVALID_NAME, "msg": "Invalid characters in branch argument"}]), 400
try: try:
with api.config["GITLOCK"].lock: with api.config["GITLOCK"].lock:
@ -1270,7 +1284,7 @@ def v0_api(api):
branch = request.args.get("branch", "master") branch = request.args.get("branch", "master")
if VALID_API_STRING.match(branch) is None: if VALID_API_STRING.match(branch) is None:
return jsonify(status=False, errors=["Invalid characters in branch argument"]), 400 return jsonify(status=False, errors=[{"id": INVALID_NAME, "msg": "Invalid characters in branch argument"}]), 400
try: try:
with api.config["GITLOCK"].lock: with api.config["GITLOCK"].lock:
@ -1297,7 +1311,7 @@ def v0_api(api):
branch = request.args.get("branch", "master") branch = request.args.get("branch", "master")
if VALID_API_STRING.match(branch) is None: if VALID_API_STRING.match(branch) is None:
return jsonify(status=False, errors=["Invalid characters in branch argument"]), 400 return jsonify(status=False, errors=[{"id": INVALID_NAME, "msg": "Invalid characters in branch argument"}]), 400
try: try:
if from_commit == "NEWEST": if from_commit == "NEWEST":
@ -1371,7 +1385,7 @@ def v0_api(api):
# No blueprint found, skip it. # No blueprint found, skip it.
if not blueprint: if not blueprint:
errors.append("%s: blueprint_not_found" % (blueprint_name)) errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: blueprint_not_found" % blueprint_name})
continue continue
# Combine modules and packages and depsolve the list # Combine modules and packages and depsolve the list
@ -1431,7 +1445,7 @@ def v0_api(api):
# No blueprint found, skip it. # No blueprint found, skip it.
if not blueprint: if not blueprint:
errors.append("%s: blueprint not found" % blueprint_name) errors.append({"id": UNKNOWN_BLUEPRINT, "msg": "%s: blueprint not found" % blueprint_name})
continue continue
# Combine modules and packages and depsolve the list # Combine modules and packages and depsolve the list
@ -1725,7 +1739,7 @@ def v0_api(api):
return jsonify(status=False, errors=["Missing POST body"]), 400 return jsonify(status=False, errors=["Missing POST body"]), 400
if "blueprint_name" not in compose: if "blueprint_name" not in compose:
errors.append("No 'blueprint_name' in the JSON request") errors.append({"id": UNKNOWN_BLUEPRINT,"msg": "No 'blueprint_name' in the JSON request"})
else: else:
blueprint_name = compose["blueprint_name"] blueprint_name = compose["blueprint_name"]

View File

@ -192,7 +192,7 @@ class ServerTestCase(unittest.TestCase):
def test_03_blueprints_info_none(self): def test_03_blueprints_info_none(self):
"""Test the /api/v0/blueprints/info route with an unknown blueprint""" """Test the /api/v0/blueprints/info route with an unknown blueprint"""
info_dict_3 = {"changes":[], info_dict_3 = {"changes":[],
"errors":["missing-blueprint: No commits for missing-blueprint.toml on the master branch."], "errors":[{"id": UNKNOWN_BLUEPRINT, "msg": "missing-blueprint: No commits for missing-blueprint.toml on the master branch."}],
"blueprints":[] "blueprints":[]
} }
resp = self.server.get("/api/v0/blueprints/info/missing-blueprint") resp = self.server.get("/api/v0/blueprints/info/missing-blueprint")