filters: add new getOneImage() helper for manifest/build

This commit adds a helper to find exactly a single image from a
given disto/type/arch description. This will be used in `manifest`,
`build` and potentially more. It is meant to support copy/paste
from the `image-builder list-images` output, so: "distro:centos-9"
is support just like "centos-9" (same for distro/imgType/arch).
This commit is contained in:
Michael Vogt 2024-11-27 09:25:00 +01:00 committed by Simon de Vlieger
parent 7f9936acc9
commit e9e8dc5256
3 changed files with 106 additions and 0 deletions

View file

@ -8,6 +8,10 @@ import (
"github.com/osbuild/images/pkg/reporegistry"
)
var (
GetOneImage = getOneImage
)
func MockOsArgs(new []string) (restore func()) {
saved := os.Args
os.Args = append([]string{"argv0"}, new...)

View file

@ -1,6 +1,11 @@
package main
import (
"fmt"
"strings"
"github.com/gobwas/glob"
"github.com/osbuild/images/pkg/distrofactory"
"github.com/osbuild/images/pkg/imagefilter"
)
@ -13,3 +18,45 @@ func newImageFilterDefault(dataDir string) (*imagefilter.ImageFilter, error) {
}
return imagefilter.New(fac, repos)
}
// should this be moved to images:imagefilter?
func getOneImage(dataDir, distroName, imgTypeStr, archStr string) (*imagefilter.Result, error) {
imageFilter, err := newImageFilterDefault(dataDir)
if err != nil {
return nil, err
}
// strip prefixes to make ib copy/paste friendly when pasting output
// from "list-images"
distroName = strings.TrimPrefix(distroName, "distro:")
imgTypeStr = strings.TrimPrefix(imgTypeStr, "type:")
archStr = strings.TrimPrefix(archStr, "arch:")
// error early when globs are used
for _, s := range []string{distroName, imgTypeStr, archStr} {
if glob.QuoteMeta(s) != s {
return nil, fmt.Errorf("cannot use globs in %q when getting a single image", s)
}
}
filterExprs := []string{
fmt.Sprintf("distro:%s", distroName),
fmt.Sprintf("arch:%s", archStr),
fmt.Sprintf("type:%s", imgTypeStr),
}
filteredResults, err := imageFilter.Filter(filterExprs...)
if err != nil {
return nil, err
}
switch len(filteredResults) {
case 0:
return nil, fmt.Errorf("cannot find image for: distro:%q type:%q arch:%q", distroName, imgTypeStr, archStr)
case 1:
return &filteredResults[0], nil
default:
// This condition should never be hit in practise as we
// disallow globs above.
// XXX: imagefilter.Result should have a String() method so
// that this output can actually show the results
return nil, fmt.Errorf("internal error: found %v results for %q %q %q", len(filteredResults), distroName, imgTypeStr, archStr)
}
}

View file

@ -0,0 +1,55 @@
package main_test
import (
"testing"
"github.com/stretchr/testify/assert"
testrepos "github.com/osbuild/images/test/data/repositories"
"github.com/osbuild/image-builder-cli/cmd/image-builder"
)
func TestGetOneImageHappy(t *testing.T) {
restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
dataDir := ""
for _, tc := range []struct {
distro, imgType, arch string
}{
{"centos-9", "qcow2", "x86_64"},
{"distro:centos-9", "qcow2", "x86_64"},
{"distro:centos-9", "type:qcow2", "x86_64"},
{"distro:centos-9", "type:qcow2", "arch:x86_64"},
} {
res, err := main.GetOneImage(dataDir, tc.distro, tc.imgType, tc.arch)
assert.NoError(t, err)
assert.Equal(t, "centos-9", res.Distro.Name())
assert.Equal(t, "qcow2", res.ImgType.Name())
assert.Equal(t, "x86_64", res.Arch.Name())
}
}
func TestGetOneImageError(t *testing.T) {
restore := main.MockNewRepoRegistry(testrepos.New)
defer restore()
dataDir := ""
for _, tc := range []struct {
distro, imgType, arch string
expectedErr string
}{
{
"unknown", "qcow2", "x86_64",
`cannot find image for: distro:"unknown" type:"qcow2" arch:"x86_64"`,
},
{
"centos*", "qcow2", "x86_64",
`cannot use globs in "centos*" when getting a single image`,
},
} {
_, err := main.GetOneImage(dataDir, tc.distro, tc.imgType, tc.arch)
assert.EqualError(t, err, tc.expectedErr)
}
}