diff --git a/cmd/image-builder/export_test.go b/cmd/image-builder/export_test.go index 0cdb7b3..c361adb 100644 --- a/cmd/image-builder/export_test.go +++ b/cmd/image-builder/export_test.go @@ -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...) diff --git a/cmd/image-builder/filters.go b/cmd/image-builder/filters.go index 480be1a..7042706 100644 --- a/cmd/image-builder/filters.go +++ b/cmd/image-builder/filters.go @@ -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) + } +} diff --git a/cmd/image-builder/filters_test.go b/cmd/image-builder/filters_test.go new file mode 100644 index 0000000..2bf984a --- /dev/null +++ b/cmd/image-builder/filters_test.go @@ -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) + } +}