diff --git a/src/pylorax/api/projects.py b/src/pylorax/api/projects.py index 9081c27e..f0500056 100644 --- a/src/pylorax/api/projects.py +++ b/src/pylorax/api/projects.py @@ -19,6 +19,7 @@ log = logging.getLogger("lorax-composer") import os from ConfigParser import ConfigParser +import fnmatch from glob import glob import time @@ -188,6 +189,24 @@ def projects_info(yb, project_names): yb.closeRpmDB() return sorted(map(yaps_to_project_info, ybl.available), key=lambda p: p["name"].lower()) +def filterVersionGlob(pkgs, version): + """Filter a list of yum package objects with a version glob + + :param pkgs: list of yum package objects + :type pkgs: list + :param version: version matching glob + :type version: str + + pkgs should be a list of all the versions of the *same* package. + Return the latest package that matches the 'version' glob. + """ + # Pick the version(s) matching the version glob + matches = [po for po in pkgs if fnmatch.fnmatchcase(po.version, version)] + if not matches: + raise RuntimeError("No package version matching %s" % version) + + # yum implements __cmd__ using verCMP so this will return the highest matching version + return max(matches) def projects_depsolve(yb, projects, groups): """Return the dependencies for a list of projects @@ -212,10 +231,21 @@ def projects_depsolve(yb, projects, groups): for name, version in projects: if not version: version = "*" - pattern = "%s-%s" % (name, version) + pattern = "%s %s" % (name, version) + + # yum.install's pattern matches the whole nevra, which can result in -* matching + # unexpected packages. So we need to implement our own version globbing. + pkgs = yb.pkgSack.searchNames([name]) + if not pkgs: + install_errors.append((name, "No package name matching %s" % name)) + continue + try: - yb.install(pattern=pattern) - except YumBaseError as e: + po = filterVersionGlob(pkgs, version) + log.debug("Chose %s as best match for %s", po.nevra, pattern) + + yb.install(po=po) + except (YumBaseError, RuntimeError) as e: install_errors.append((pattern, str(e))) # Were there problems installing these packages? diff --git a/tests/pylorax/test_projects.py b/tests/pylorax/test_projects.py index 94abd68b..76528109 100644 --- a/tests/pylorax/test_projects.py +++ b/tests/pylorax/test_projects.py @@ -23,6 +23,7 @@ import tempfile import unittest from yum.Errors import YumBaseError +from yum.packages import PackageObject from pylorax.sysutils import joinpaths from pylorax.api.config import configure, make_yum_dirs @@ -30,7 +31,7 @@ from pylorax.api.projects import api_time, api_changelog, yaps_to_project, yaps_ 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, dep_evra, dep_nevra from pylorax.api.projects import repo_to_source, get_repo_sources, delete_repo_source, source_to_repo -from pylorax.api.projects import yum_repo_to_file_repo +from pylorax.api.projects import yum_repo_to_file_repo, filterVersionGlob from pylorax.api.yumbase import get_base_object @@ -59,6 +60,15 @@ class TM(object): release = "release" arch = "arch" +def NewPackageObject(name, epoch, version, release, arch): + po = PackageObject() + po.name = name + po.epoch = epoch + po.version = version + po.release = release + po.arch = arch + po.pkgtup = (po.name, po.arch, po.epoch, po.version, po.release) + return po class ProjectsTest(unittest.TestCase): @classmethod @@ -204,6 +214,52 @@ class ProjectsTest(unittest.TestCase): with self.assertRaises(ProjectsError): projects_depsolve(self.yb, [("nada-package", "*.*")], []) + def test_projects_depsolve_glob(self): + """Test that depsolving with a '*' version glob doesn't glob package names""" + deps = projects_depsolve(self.yb, [("python", "*")], []) + self.assertTrue(len(deps) > 1) + self.assertTrue("python" in [dep["name"] for dep in deps]) + self.assertTrue("python-blivet" not in [dep["name"] for dep in deps]) + + def test_projects_filterVersionGlob(self): + """Test the filterVersionGlob function""" + pkgs = [NewPackageObject("foopkg", "0", "1.1.5", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "1.2.0", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "2.4.3", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "2.4.17", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "2.10.0", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "3.0.0", "el7", "x86_64")] + + # 1.1.5 version + self.assertEqual(filterVersionGlob(pkgs, "1.1.5"), pkgs[0]) + + # Newest 1.x.x version + self.assertEqual(filterVersionGlob(pkgs, "1.*.*"), pkgs[1]) + + # Newest 2.4.x version + self.assertEqual(filterVersionGlob(pkgs, "2.4.*"), pkgs[3]) + + # Newest 2.x version + self.assertEqual(filterVersionGlob(pkgs, "2.*"), pkgs[4]) + + # Newest version + self.assertEqual(filterVersionGlob(pkgs, "*"), pkgs[5]) + + def test_projects_filterVersionGlob_epoch(self): + """Test the filterVersionGlob function with epoch""" + pkgs = [NewPackageObject("foopkg", "2", "1.1.5", "el7", "x86_64"), + NewPackageObject("foopkg", "2", "1.2.0", "el7", "x86_64"), + NewPackageObject("foopkg", "0", "2.4.3", "el7", "x86_64")] + + # 1.1.5 version + self.assertEqual(filterVersionGlob(pkgs, "1.1.5"), pkgs[0]) + + # Newest 1.x.x version + self.assertEqual(filterVersionGlob(pkgs, "1.*.*"), pkgs[1]) + + # Newest version + self.assertEqual(filterVersionGlob(pkgs, "*"), pkgs[1]) + def test_modules_list(self): modules = modules_list(self.yb, None)