From 1e7335a506e1cf61f71464e0150c1f2c12955c5a Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Wed, 8 Nov 2017 17:38:12 -0800 Subject: [PATCH] Add /recipes/info route and tests --- src/pylorax/api/v0.py | 52 ++++++++++++++++++++++++++++++++++- tests/pylorax/test_server.py | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/pylorax/api/v0.py b/src/pylorax/api/v0.py index ff33a053..803acc07 100644 --- a/src/pylorax/api/v0.py +++ b/src/pylorax/api/v0.py @@ -21,7 +21,8 @@ from pykickstart.parser import KickstartParser from pykickstart.version import makeVersion, RHEL7 from pylorax.api.crossdomain import crossdomain -from pylorax.api.recipes import list_branch_files +from pylorax.api.recipes import list_branch_files, read_recipe_commit, recipe_filename +from pylorax.api.workspace import workspace_read from pylorax.creator import DRACUT_DEFAULT, mount_boot_part_over_root from pylorax.creator import make_appliance, make_image, make_livecd, make_live_images from pylorax.creator import make_runtime, make_squashfs @@ -60,3 +61,52 @@ def v0_api(api): with api.config["GITLOCK"].lock: recipes = map(lambda f: f[:-5], list_branch_files(api.config["GITLOCK"].repo, "master")) return jsonify(recipes=recipes, limit=limit, offset=offset, total=len(recipes)) + + @api.route("/api/v0/recipes/info/") + @crossdomain(origin="*") + def v0_recipes_info(recipe_names): + """Return the contents of the recipe, or a list of recipes""" + recipes = [] + changes = [] + errors = [] + for recipe_name in [n.strip() for n in recipe_names.split(",")]: + exceptions = [] + filename = recipe_filename(recipe_name) + # Get the workspace version (if it exists) + try: + with api.config["GITLOCK"].lock: + ws_recipe = workspace_read(api.config["GITLOCK"].repo, "master", filename) + except Exception as e: + ws_recipe = None + exceptions.append(str(e)) + + # Get the git version (if it exists) + try: + with api.config["GITLOCK"].lock: + git_recipe = read_recipe_commit(api.config["GITLOCK"].repo, "master", filename) + except Exception as e: + git_recipe = None + exceptions.append(str(e)) + + if not ws_recipe and not git_recipe: + # Neither recipe, return an error + errors.append({"recipe":recipe_name, "msg":", ".join(exceptions)}) + elif ws_recipe and not git_recipe: + # No git recipe, return the workspace recipe + changes.append({"name":recipe_name, "changed":True}) + recipes.append(ws_recipe) + elif not ws_recipe and git_recipe: + # No workspace recipe, no change, return the git recipe + changes.append({"name":recipe_name, "changed":False}) + recipes.append(git_recipe) + else: + # Both exist, maybe changed, return the workspace recipe + changes.append({"name":recipe_name, "changed":ws_recipe == git_recipe}) + recipes.append(ws_recipe) + + # Sort all the results by case-insensitive recipe name + changes = sorted(changes, key=lambda c: c["name"].lower()) + recipes = sorted(recipes, key=lambda r: r["name"].lower()) + errors = sorted(errors, key=lambda e: e["recipe"].lower()) + + return jsonify(changes=changes, recipes=recipes, errors=errors) diff --git a/tests/pylorax/test_server.py b/tests/pylorax/test_server.py index cb9197dd..9c013bfb 100644 --- a/tests/pylorax/test_server.py +++ b/tests/pylorax/test_server.py @@ -56,3 +56,56 @@ class ServerTestCase(unittest.TestCase): resp = self.server.get("/api/v0/recipes/list") data = json.loads(resp.data) self.assertEqual(data, list_dict) + + def test_recipes_info(self): + """Test the /api/v0/recipes/info route""" + info_dict_1 = {"changes":[{"changed":False, "name":"http-server"}], + "errors":[], + "recipes":[{"description":"An example http server with PHP and MySQL support.", + "modules":[{"name":"httpd", "version":"2.4.*"}, + {"name":"mod_auth_kerb", "version":"5.4"}, + {"name":"mod_ssl", "version":"2.4.*"}, + {"name":"php", "version":"5.4.*"}, + {"name": "php-mysql", "version":"5.4.*"}], + "name":"http-server", + "packages": [{"name":"openssh-server", "version": "6.6.*"}, + {"name": "rsync", "version": "3.0.*"}, + {"name": "tmux", "version": "2.2"}], + "version": "0.0.2"}]} + resp = self.server.get("/api/v0/recipes/info/http-server") + data = json.loads(resp.data) + self.assertEqual(data, info_dict_1) + + info_dict_2 = {"changes":[{"changed":False, "name":"glusterfs"}, + {"changed":False, "name":"http-server"}], + "errors":[], + "recipes":[{"description": "An example GlusterFS server with samba", + "modules":[{"name":"glusterfs", "version":"3.7.*"}, + {"name":"glusterfs-cli", "version":"3.7.*"}], + "name":"glusterfs", + "packages":[{"name":"samba", "version":"4.2.*"}], + "version": "0.0.1"}, + {"description":"An example http server with PHP and MySQL support.", + "modules":[{"name":"httpd", "version":"2.4.*"}, + {"name":"mod_auth_kerb", "version":"5.4"}, + {"name":"mod_ssl", "version":"2.4.*"}, + {"name":"php", "version":"5.4.*"}, + {"name": "php-mysql", "version":"5.4.*"}], + "name":"http-server", + "packages": [{"name":"openssh-server", "version": "6.6.*"}, + {"name": "rsync", "version": "3.0.*"}, + {"name": "tmux", "version": "2.2"}], + "version": "0.0.2"}, + ]} + resp = self.server.get("/api/v0/recipes/info/http-server,glusterfs") + data = json.loads(resp.data) + self.assertEqual(data, info_dict_2) + + info_dict_3 = {"changes":[], + "errors":[{"recipe":"missing-recipe", "msg":"ggit-error: the path 'missing-recipe.toml' does not exist in the given tree (-3)"}], + "recipes":[] + } + resp = self.server.get("/api/v0/recipes/info/missing-recipe") + data = json.loads(resp.data) + self.assertEqual(data, info_dict_3) +