From 6b4bf92e7ff247766569f7f20a9159283cffa86a Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Wed, 22 Nov 2017 14:46:43 -0800 Subject: [PATCH] Add /recipes/freeze route and tests. --- src/pylorax/api/projects.py | 13 +++++++ src/pylorax/api/v0.py | 64 ++++++++++++++++++++++++++++++++-- tests/pylorax/test_projects.py | 10 +++++- tests/pylorax/test_server.py | 12 +++++++ 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/pylorax/api/projects.py b/src/pylorax/api/projects.py index 82ffd9ae..7092b7e8 100644 --- a/src/pylorax/api/projects.py +++ b/src/pylorax/api/projects.py @@ -133,6 +133,19 @@ def yaps_to_module(yaps): "group_type": "rpm"} +def dep_evra(dep): + """Return the epoch:version-release.arch for the dep + + :param dep: dependency dict + :type dep: dict + :returns: epoch:version-release.arch + :rtype: str + """ + if dep["epoch"] == "0": + return dep["version"]+"-"+dep["release"]+"."+dep["arch"] + else: + return dep["epoch"]+":"+dep["version"]+"-"+dep["release"]+"."+dep["arch"] + def projects_list(yb): """Return a list of projects diff --git a/src/pylorax/api/v0.py b/src/pylorax/api/v0.py index ee961461..b196083a 100644 --- a/src/pylorax/api/v0.py +++ b/src/pylorax/api/v0.py @@ -24,11 +24,11 @@ from pykickstart.parser import KickstartParser from pykickstart.version import makeVersion, RHEL7 from pylorax.api.crossdomain import crossdomain -from pylorax.api.projects import projects_list, projects_info, projects_depsolve +from pylorax.api.projects import projects_list, projects_info, projects_depsolve, dep_evra from pylorax.api.projects import modules_list, modules_info, ProjectsError from pylorax.api.recipes import 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 tag_recipe_commit, recipe_diff +from pylorax.api.recipes import tag_recipe_commit, recipe_diff, Recipe, RecipePackage, RecipeModule from pylorax.api.workspace import workspace_read, workspace_write, workspace_delete from pylorax.creator import DRACUT_DEFAULT, mount_boot_part_over_root from pylorax.creator import make_appliance, make_image, make_livecd, make_live_images @@ -282,6 +282,66 @@ def v0_api(api): diff = recipe_diff(old_recipe, new_recipe) return jsonify(diff=diff) + @api.route("/api/v0/recipes/freeze/") + @crossdomain(origin="*") + def v0_recipes_freeze(recipe_names): + """Return the recipe with the exact modules and packages selected by depsolve""" + recipes = [] + errors = [] + for recipe_name in [n.strip() for n in sorted(recipe_names.split(","), key=lambda n: n.lower())]: + # get the recipe + # Get the workspace version (if it exists) + recipe = None + try: + with api.config["GITLOCK"].lock: + recipe = workspace_read(api.config["GITLOCK"].repo, "master", recipe_name) + except Exception: + pass + + if not recipe: + # No workspace version, get the git version (if it exists) + try: + with api.config["GITLOCK"].lock: + recipe = read_recipe_commit(api.config["GITLOCK"].repo, "master", recipe_name) + except Exception as e: + errors.append({"recipe":recipe_name, "msg":str(e)}) + log.error("(v0_recipes_depsolve) %s", str(e)) + + # No recipe found, skip it. + if not recipe: + errors.append({"recipe":recipe_name, "msg":"Recipe not found"}) + continue + + # Combine modules and packages and depsolve the list + # TODO include the version/glob in the depsolving + module_names = map(lambda m: m["name"], recipe["modules"] or []) + package_names = map(lambda p: p["name"], recipe["packages"] or []) + projects = sorted(set(module_names+package_names), key=lambda n: n.lower()) + deps = [] + try: + with api.config["YUMLOCK"].lock: + deps = projects_depsolve(api.config["YUMLOCK"].yb, projects) + except ProjectsError as e: + errors.append({"recipe":recipe_name, "msg":str(e)}) + log.error("(v0_recipes_depsolve) %s", str(e)) + + # Change the recipe's modules and packages to use the depsolved version + new_modules = [] + new_packages = [] + for dep in deps: + if dep["name"] in package_names: + new_packages.append(RecipePackage(dep["name"], dep_evra(dep))) + elif dep["name"] in module_names: + new_modules.append(RecipeModule(dep["name"], dep_evra(dep))) + + recipes.append({"recipe":Recipe(recipe["name"], + recipe["description"], + recipe["version"], + new_modules, + new_packages)}) + + return jsonify(recipes=recipes, errors=errors) + @api.route("/api/v0/recipes/depsolve/") @crossdomain(origin="*") def v0_recipes_depsolve(recipe_names): diff --git a/tests/pylorax/test_projects.py b/tests/pylorax/test_projects.py index e48d85d0..664d4d5e 100644 --- a/tests/pylorax/test_projects.py +++ b/tests/pylorax/test_projects.py @@ -21,7 +21,7 @@ import unittest from pylorax.api.config import configure from pylorax.api.projects import api_time, api_changelog, yaps_to_project, yaps_to_project_info from pylorax.api.projects import tm_to_dep, yaps_to_module, projects_list, projects_info, projects_depsolve -from pylorax.api.projects import modules_list, modules_info, ProjectsError +from pylorax.api.projects import modules_list, modules_info, ProjectsError, dep_evra from pylorax.api.yumbase import get_base_object @@ -119,6 +119,14 @@ class ProjectsTest(unittest.TestCase): y = Yaps() self.assertEqual(yaps_to_module(y), result) + def test_dep_evra(self): + dep = {"arch": "noarch", + "epoch": "0", + "name": "basesystem", + "release": "7.el7", + "version": "10.0"} + self.assertEqual(dep_evra(dep), "10.0-7.el7.noarch") + def test_projects_list(self): projects = projects_list(self.yb) self.assertEqual(len(projects) > 10, True) diff --git a/tests/pylorax/test_server.py b/tests/pylorax/test_server.py index e5a43405..c4bd6552 100644 --- a/tests/pylorax/test_server.py +++ b/tests/pylorax/test_server.py @@ -370,6 +370,18 @@ class ServerTestCase(unittest.TestCase): self.assertEqual(recipes[0]["recipe"]["name"], "glusterfs") self.assertEqual(len(recipes[0]["dependencies"]) > 10, True) + def test_15_recipes_freeze(self): + """Test /api/v0/recipes/freeze/""" + resp = self.server.get("/api/v0/recipes/freeze/glusterfs") + data = json.loads(resp.data) + self.assertNotEqual(data, None) + recipes = data.get("recipes") + self.assertNotEqual(recipes, None) + self.assertEqual(len(recipes), 1) + self.assertEqual(recipes[0]["recipe"]["name"], "glusterfs") + evra = recipes[0]["recipe"]["modules"][0]["version"] + self.assertEqual(len(evra) > 10, True) + def test_projects_list(self): """Test /api/v0/projects/list""" resp = self.server.get("/api/v0/projects/list")