From f6194f3231a5b2be43073c80d954254030bb6859 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 12 Nov 2024 16:43:02 +0100 Subject: [PATCH] imagefilter: add support to filter for `pkg:` prefixes This commit adds support to filter image based on (unresolved) packages. E.g.: ``` $ ./image-builder list-images --filter pkg:gdisk ... rhel-9.6 type:vhd arch:x86_64 ``` Based on an idea from Achilleas-k, many thanks. --- pkg/imagefilter/filter.go | 31 +++++++++++++++++++++++++++---- pkg/imagefilter/filter_test.go | 3 +++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/pkg/imagefilter/filter.go b/pkg/imagefilter/filter.go index 748df4b86c..5436ec8f76 100644 --- a/pkg/imagefilter/filter.go +++ b/pkg/imagefilter/filter.go @@ -7,6 +7,7 @@ import ( "github.com/gobwas/glob" + "github.com/osbuild/images/pkg/blueprint" "github.com/osbuild/images/pkg/distro" ) @@ -50,7 +51,7 @@ func newFilter(sl ...string) (*filter, error) { } var supportedFilters = []string{ - "", "distro", "arch", "type", "bootmode", + "", "distro", "arch", "type", "bootmode", "pkg", } type term struct { @@ -66,19 +67,20 @@ type filter struct { // Matches returns true if the given (distro,arch,imgType) tuple matches // the filter expressions -func (fl filter) Matches(distro distro.Distro, arch distro.Arch, imgType distro.ImageType) bool { +func (fl filter) Matches(dist distro.Distro, arch distro.Arch, imgType distro.ImageType) bool { m := true + for _, term := range fl.terms { switch term.prefix { case "": // no prefix, do a "fuzzy" search accross the common // things users may want - m1 := term.pattern.Match(distro.Name()) + m1 := term.pattern.Match(dist.Name()) m2 := term.pattern.Match(arch.Name()) m3 := term.pattern.Match(imgType.Name()) m = m && (m1 || m2 || m3) case "distro": - m = m && term.pattern.Match(distro.Name()) + m = m && term.pattern.Match(dist.Name()) case "arch": m = m && term.pattern.Match(arch.Name()) case "type": @@ -86,7 +88,28 @@ func (fl filter) Matches(distro distro.Distro, arch distro.Arch, imgType distro. // mostly here to show how flexible this is case "bootmode": m = m && term.pattern.Match(imgType.BootMode().String()) + case "pkg": + m = m && containsPackages(imgType, term.pattern) } } return m } + +func containsPackages(imgType distro.ImageType, pattern glob.Glob) bool { + var bp blueprint.Blueprint + manifest, _, err := imgType.Manifest(&bp, distro.ImageOptions{}, nil, 0) + if err != nil { + return false + } + + for _, pkgSets := range manifest.GetPackageSetChains() { + for _, pkgSet := range pkgSets { + for _, pkg := range pkgSet.Include { + if pattern.Match(pkg) { + return true + } + } + } + } + return false +} diff --git a/pkg/imagefilter/filter_test.go b/pkg/imagefilter/filter_test.go index eb34528971..1886adeef5 100644 --- a/pkg/imagefilter/filter_test.go +++ b/pkg/imagefilter/filter_test.go @@ -40,6 +40,9 @@ func TestImageFilterFilter(t *testing.T) { // bootmode: prefix {[]string{"bootmode:uefi"}, "test-distro-1", "test_arch3", "qcow2", false}, {[]string{"bootmode:hybrid"}, "test-distro-1", "test_arch3", "qcow2", true}, + // pkg: prefix + {[]string{"pkg:rando-no"}, "test-distro-1", "test_arch3", "qcow2", false}, + {[]string{"pkg:dep-package1"}, "test-distro-1", "test_arch3", "qcow2", true}, // multiple filters are AND {[]string{"distro:test-distro-1", "type:ami"}, "test-distro-1", "test_arch3", "qcow2", false}, {[]string{"distro:test-distro-1", "type:qcow2"}, "test-distro-1", "test_arch3", "qcow2", true},