summaryrefslogtreecommitdiff
path: root/libpod/image/search.go
diff options
context:
space:
mode:
authorValentin Rothberg <rothberg@redhat.com>2021-04-22 08:01:12 +0200
committerValentin Rothberg <rothberg@redhat.com>2021-05-05 11:30:12 +0200
commit0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21 (patch)
tree192e52054de2abf0c92d83ecdbc71d498c2ec947 /libpod/image/search.go
parent8eefca5a257121b177562742c972e39e1686140d (diff)
downloadpodman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.tar.gz
podman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.tar.bz2
podman-0f7d54b0260c1be992ee3b9cee359ef3a9e8bd21.zip
migrate Podman to containers/common/libimage
Migrate the Podman code base over to `common/libimage` which replaces `libpod/image` and a lot of glue code entirely. Note that I tried to leave bread crumbs for changed tests. Miscellaneous changes: * Some errors yield different messages which required to alter some tests. * I fixed some pre-existing issues in the code. Others were marked as `//TODO`s to prevent the PR from exploding. * The `NamesHistory` of an image is returned as is from the storage. Previously, we did some filtering which I think is undesirable. Instead we should return the data as stored in the storage. * Touched handlers use the ABI interfaces where possible. * Local image resolution: previously Podman would match "foo" on "myfoo". This behaviour has been changed and Podman will now only match on repository boundaries such that "foo" would match "my/foo" but not "myfoo". I consider the old behaviour to be a bug, at the very least an exotic corner case. * Futhermore, "foo:none" does *not* resolve to a local image "foo" without tag anymore. It's a hill I am (almost) willing to die on. * `image prune` prints the IDs of pruned images. Previously, in some cases, the names were printed instead. The API clearly states ID, so we should stick to it. * Compat endpoint image removal with _force_ deletes the entire not only the specified tag. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'libpod/image/search.go')
-rw-r--r--libpod/image/search.go318
1 files changed, 0 insertions, 318 deletions
diff --git a/libpod/image/search.go b/libpod/image/search.go
deleted file mode 100644
index 714551e6e..000000000
--- a/libpod/image/search.go
+++ /dev/null
@@ -1,318 +0,0 @@
-package image
-
-import (
- "context"
- "fmt"
- "strconv"
- "strings"
- "sync"
-
- "github.com/containers/image/v5/docker"
- "github.com/containers/image/v5/transports/alltransports"
- "github.com/containers/image/v5/types"
- sysreg "github.com/containers/podman/v3/pkg/registries"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "golang.org/x/sync/semaphore"
-)
-
-const (
- descriptionTruncLength = 44
- maxQueries = 25
- maxParallelSearches = int64(6)
-)
-
-// SearchResult is holding image-search related data.
-type SearchResult struct {
- // Index is the image index (e.g., "docker.io" or "quay.io")
- Index string
- // Name is the canonical name of the image (e.g., "docker.io/library/alpine").
- Name string
- // Description of the image.
- Description string
- // Stars is the number of stars of the image.
- Stars int
- // Official indicates if it's an official image.
- Official string
- // Automated indicates if the image was created by an automated build.
- Automated string
- // Tag is the image tag
- Tag string
-}
-
-// SearchOptions are used to control the behaviour of SearchImages.
-type SearchOptions struct {
- // Filter allows to filter the results.
- Filter SearchFilter
- // Limit limits the number of queries per index (default: 25). Must be
- // greater than 0 to overwrite the default value.
- Limit int
- // NoTrunc avoids the output to be truncated.
- NoTrunc bool
- // Authfile is the path to the authentication file.
- Authfile string
- // InsecureSkipTLSVerify allows to skip TLS verification.
- InsecureSkipTLSVerify types.OptionalBool
- // ListTags returns the search result with available tags
- ListTags bool
-}
-
-// SearchFilter allows filtering the results of SearchImages.
-type SearchFilter struct {
- // Stars describes the minimal amount of starts of an image.
- Stars int
- // IsAutomated decides if only images from automated builds are displayed.
- IsAutomated types.OptionalBool
- // IsOfficial decides if only official images are displayed.
- IsOfficial types.OptionalBool
-}
-
-// SearchImages searches images based on term and the specified SearchOptions
-// in all registries.
-func SearchImages(term string, options SearchOptions) ([]SearchResult, error) {
- registry := ""
-
- // Try to extract a registry from the specified search term. We
- // consider everything before the first slash to be the registry. Note
- // that we cannot use the reference parser from the containers/image
- // library as the search term may container arbitrary input such as
- // wildcards. See bugzilla.redhat.com/show_bug.cgi?id=1846629.
- if spl := strings.SplitN(term, "/", 2); len(spl) > 1 {
- registry = spl[0]
- term = spl[1]
- }
-
- registries, err := getRegistries(registry)
- if err != nil {
- return nil, err
- }
-
- // searchOutputData is used as a return value for searching in parallel.
- type searchOutputData struct {
- data []SearchResult
- err error
- }
-
- // Let's follow Firefox by limiting parallel downloads to 6.
- sem := semaphore.NewWeighted(maxParallelSearches)
- wg := sync.WaitGroup{}
- wg.Add(len(registries))
- data := make([]searchOutputData, len(registries))
-
- searchImageInRegistryHelper := func(index int, registry string) {
- defer sem.Release(1)
- defer wg.Done()
- searchOutput, err := searchImageInRegistry(term, registry, options)
- data[index] = searchOutputData{data: searchOutput, err: err}
- }
-
- ctx := context.Background()
- for i := range registries {
- if err := sem.Acquire(ctx, 1); err != nil {
- return nil, err
- }
- go searchImageInRegistryHelper(i, registries[i])
- }
-
- wg.Wait()
- results := []SearchResult{}
- var lastError error
- for _, d := range data {
- if d.err != nil {
- if lastError != nil {
- logrus.Errorf("%v", lastError)
- }
- lastError = d.err
- continue
- }
- results = append(results, d.data...)
- }
- if len(results) > 0 {
- return results, nil
- }
- return results, lastError
-}
-
-// getRegistries returns the list of registries to search, depending on an optional registry specification
-func getRegistries(registry string) ([]string, error) {
- var registries []string
- if registry != "" {
- registries = append(registries, registry)
- } else {
- var err error
- registries, err = sysreg.GetRegistries()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries to search")
- }
- }
- return registries, nil
-}
-
-func searchImageInRegistry(term string, registry string, options SearchOptions) ([]SearchResult, error) {
- // Max number of queries by default is 25
- limit := maxQueries
- if options.Limit > 0 {
- limit = options.Limit
- }
-
- sc := GetSystemContext("", options.Authfile, false)
- sc.DockerInsecureSkipTLSVerify = options.InsecureSkipTLSVerify
- // FIXME: Set this more globally. Probably no reason not to have it in
- // every types.SystemContext, and to compute the value just once in one
- // place.
- sc.SystemRegistriesConfPath = sysreg.SystemRegistriesConfPath()
- if options.ListTags {
- results, err := searchRepositoryTags(registry, term, sc, options)
- if err != nil {
- return []SearchResult{}, err
- }
- return results, nil
- }
-
- results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit)
- if err != nil {
- return []SearchResult{}, err
- }
- index := registry
- arr := strings.Split(registry, ".")
- if len(arr) > 2 {
- index = strings.Join(arr[len(arr)-2:], ".")
- }
-
- // limit is the number of results to output
- // if the total number of results is less than the limit, output all
- // if the limit has been set by the user, output those number of queries
- limit = maxQueries
- if len(results) < limit {
- limit = len(results)
- }
- if options.Limit != 0 {
- limit = len(results)
- if options.Limit < len(results) {
- limit = options.Limit
- }
- }
-
- paramsArr := []SearchResult{}
- for i := 0; i < limit; i++ {
- // Check whether query matches filters
- if !(options.Filter.matchesAutomatedFilter(results[i]) && options.Filter.matchesOfficialFilter(results[i]) && options.Filter.matchesStarFilter(results[i])) {
- continue
- }
- official := ""
- if results[i].IsOfficial {
- official = "[OK]"
- }
- automated := ""
- if results[i].IsAutomated {
- automated = "[OK]"
- }
- description := strings.Replace(results[i].Description, "\n", " ", -1)
- if len(description) > 44 && !options.NoTrunc {
- description = description[:descriptionTruncLength] + "..."
- }
- name := registry + "/" + results[i].Name
- if index == "docker.io" && !strings.Contains(results[i].Name, "/") {
- name = index + "/library/" + results[i].Name
- }
- params := SearchResult{
- Index: index,
- Name: name,
- Description: description,
- Official: official,
- Automated: automated,
- Stars: results[i].StarCount,
- }
- paramsArr = append(paramsArr, params)
- }
- return paramsArr, nil
-}
-
-func searchRepositoryTags(registry, term string, sc *types.SystemContext, options SearchOptions) ([]SearchResult, error) {
- dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
- imageRef, err := alltransports.ParseImageName(fmt.Sprintf("%s/%s", registry, term))
- if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
- return nil, errors.Errorf("reference %q must be a docker reference", term)
- } else if err != nil {
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, fmt.Sprintf("%s/%s", registry, term)))
- if err != nil {
- return nil, errors.Errorf("reference %q must be a docker reference", term)
- }
- }
- tags, err := docker.GetRepositoryTags(context.TODO(), sc, imageRef)
- if err != nil {
- return nil, errors.Errorf("error getting repository tags: %v", err)
- }
- limit := maxQueries
- if len(tags) < limit {
- limit = len(tags)
- }
- if options.Limit != 0 {
- limit = len(tags)
- if options.Limit < limit {
- limit = options.Limit
- }
- }
- paramsArr := []SearchResult{}
- for i := 0; i < limit; i++ {
- params := SearchResult{
- Name: imageRef.DockerReference().Name(),
- Tag: tags[i],
- }
- paramsArr = append(paramsArr, params)
- }
- return paramsArr, nil
-}
-
-// ParseSearchFilter turns the filter into a SearchFilter that can be used for
-// searching images.
-func ParseSearchFilter(filter []string) (*SearchFilter, error) {
- sFilter := new(SearchFilter)
- for _, f := range filter {
- arr := strings.SplitN(f, "=", 2)
- switch arr[0] {
- case "stars":
- if len(arr) < 2 {
- return nil, errors.Errorf("invalid `stars` filter %q, should be stars=<value>", filter)
- }
- stars, err := strconv.Atoi(arr[1])
- if err != nil {
- return nil, errors.Wrapf(err, "incorrect value type for stars filter")
- }
- sFilter.Stars = stars
- case "is-automated":
- if len(arr) == 2 && arr[1] == "false" {
- sFilter.IsAutomated = types.OptionalBoolFalse
- } else {
- sFilter.IsAutomated = types.OptionalBoolTrue
- }
- case "is-official":
- if len(arr) == 2 && arr[1] == "false" {
- sFilter.IsOfficial = types.OptionalBoolFalse
- } else {
- sFilter.IsOfficial = types.OptionalBoolTrue
- }
- default:
- return nil, errors.Errorf("invalid filter type %q", f)
- }
- }
- return sFilter, nil
-}
-
-func (f *SearchFilter) matchesStarFilter(result docker.SearchResult) bool {
- return result.StarCount >= f.Stars
-}
-
-func (f *SearchFilter) matchesAutomatedFilter(result docker.SearchResult) bool {
- if f.IsAutomated != types.OptionalBoolUndefined {
- return result.IsAutomated == (f.IsAutomated == types.OptionalBoolTrue)
- }
- return true
-}
-
-func (f *SearchFilter) matchesOfficialFilter(result docker.SearchResult) bool {
- if f.IsOfficial != types.OptionalBoolUndefined {
- return result.IsOfficial == (f.IsOfficial == types.OptionalBoolTrue)
- }
- return true
-}