Fix depsolve version globbing

The blueprint version glob was being applied to the whole package NEVRA
by yum (it lacks a separate API for just globbing versions), so this
implements that in filterVersionGlob using fnmatchcase on the package
names, and the yum package verGT comparison on the versions for the
selected package.

Also includes tests.

Resolves: rhbz#1628114
This commit is contained in:
Brian C. Lane 2018-09-17 16:10:38 -07:00
parent 9685fdd7aa
commit 2a85694c9b
2 changed files with 90 additions and 4 deletions

View File

@ -19,6 +19,7 @@ log = logging.getLogger("lorax-composer")
import os import os
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
import fnmatch
from glob import glob from glob import glob
import time import time
@ -188,6 +189,24 @@ def projects_info(yb, project_names):
yb.closeRpmDB() yb.closeRpmDB()
return sorted(map(yaps_to_project_info, ybl.available), key=lambda p: p["name"].lower()) 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): def projects_depsolve(yb, projects, groups):
"""Return the dependencies for a list of projects """Return the dependencies for a list of projects
@ -212,10 +231,21 @@ def projects_depsolve(yb, projects, groups):
for name, version in projects: for name, version in projects:
if not version: if not version:
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: try:
yb.install(pattern=pattern) po = filterVersionGlob(pkgs, version)
except YumBaseError as e: 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))) install_errors.append((pattern, str(e)))
# Were there problems installing these packages? # Were there problems installing these packages?

View File

@ -23,6 +23,7 @@ import tempfile
import unittest import unittest
from yum.Errors import YumBaseError from yum.Errors import YumBaseError
from yum.packages import PackageObject
from pylorax.sysutils import joinpaths from pylorax.sysutils import joinpaths
from pylorax.api.config import configure, make_yum_dirs 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 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 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 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 from pylorax.api.yumbase import get_base_object
@ -59,6 +60,15 @@ class TM(object):
release = "release" release = "release"
arch = "arch" 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): class ProjectsTest(unittest.TestCase):
@classmethod @classmethod
@ -204,6 +214,52 @@ class ProjectsTest(unittest.TestCase):
with self.assertRaises(ProjectsError): with self.assertRaises(ProjectsError):
projects_depsolve(self.yb, [("nada-package", "*.*")], []) 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): def test_modules_list(self):
modules = modules_list(self.yb, None) modules = modules_list(self.yb, None)