diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2019-02-18 16:14:34 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2019-02-20 10:25:25 +0100 |
commit | 87c9f4cc226a8b709d39de473f09dd341b1a4fc3 (patch) | |
tree | 746b95ebc90096e019f53c1d3b512c196e9e5652 /cmd/podman/search.go | |
parent | b5c0d15683de4c4d1f604cc34378c733295982fb (diff) | |
download | podman-87c9f4cc226a8b709d39de473f09dd341b1a4fc3.tar.gz podman-87c9f4cc226a8b709d39de473f09dd341b1a4fc3.tar.bz2 podman-87c9f4cc226a8b709d39de473f09dd341b1a4fc3.zip |
podman-search: refactor code to libpod/image/search.go
Refactor the image-search logic from cmd/podman/search.go to
libpod/image/search.go and update podman-search and the Varlink API to
use it.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'cmd/podman/search.go')
-rw-r--r-- | cmd/podman/search.go | 267 |
1 files changed, 13 insertions, 254 deletions
diff --git a/cmd/podman/search.go b/cmd/podman/search.go index 02f171a68..a772827a6 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -1,22 +1,14 @@ package main import ( - "context" - "reflect" - "strconv" "strings" - "sync" - "github.com/containers/image/docker" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/formats" - "github.com/containers/libpod/libpod/common" - sysreg "github.com/containers/libpod/pkg/registries" + "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "golang.org/x/sync/semaphore" ) const ( @@ -56,30 +48,6 @@ func init() { flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)") } -type searchParams struct { - Index string - Name string - Description string - Stars int - Official string - Automated string -} - -type searchOpts struct { - filter []string - limit int - noTrunc bool - format string - authfile string - insecureSkipTLSVerify types.OptionalBool -} - -type searchFilterParams struct { - stars int - isAutomated *bool - isOfficial *bool -} - func searchCmd(c *cliconfig.SearchValues) error { args := c.InputArgs if len(args) > 1 { @@ -90,37 +58,24 @@ func searchCmd(c *cliconfig.SearchValues) error { } term := args[0] - // Check if search term has a registry in it - registry, err := sysreg.GetRegistry(term) - if err != nil { - return errors.Wrapf(err, "error getting registry from %q", term) - } - if registry != "" { - term = term[len(registry)+1:] - } - - format := genSearchFormat(c.Format) - opts := searchOpts{ - format: format, - noTrunc: c.NoTrunc, - limit: c.Limit, - filter: c.Filter, - authfile: getAuthFile(c.Authfile), + searchOptions := image.SearchOptions{ + NoTrunc: c.NoTrunc, + Limit: c.Limit, + Filter: c.Filter, + Authfile: getAuthFile(c.Authfile), } if c.Flag("tls-verify").Changed { - opts.insecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) - } - registries, err := getRegistries(registry) - if err != nil { - return err + searchOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!c.TlsVerify) } - filter, err := parseSearchFilter(&opts) + results, err := image.SearchImages(term, searchOptions) if err != nil { return err } - - return generateSearchOutput(term, registries, opts, *filter) + format := genSearchFormat(c.Format) + out := formats.StdoutTemplateArray{Output: searchToGeneric(results), Template: format, Fields: results[0].HeaderMap()} + formats.Writer(out).Out() + return nil } func genSearchFormat(format string) string { @@ -132,205 +87,9 @@ func genSearchFormat(format string) string { return "table {{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\t" } -func searchToGeneric(params []searchParams) (genericParams []interface{}) { +func searchToGeneric(params []image.SearchResult) (genericParams []interface{}) { for _, v := range params { genericParams = append(genericParams, interface{}(v)) } return genericParams } - -func (s *searchParams) headerMap() map[string]string { - v := reflect.Indirect(reflect.ValueOf(s)) - values := make(map[string]string, v.NumField()) - - for i := 0; i < v.NumField(); i++ { - key := v.Type().Field(i).Name - value := key - values[key] = strings.ToUpper(splitCamelCase(value)) - } - return values -} - -// 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 getSearchOutput(term string, registry string, opts searchOpts, filter searchFilterParams) ([]searchParams, error) { - // Max number of queries by default is 25 - limit := maxQueries - if opts.limit != 0 { - limit = opts.limit - } - - sc := common.GetSystemContext("", opts.authfile, false) - sc.DockerInsecureSkipTLSVerify = opts.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() - results, err := docker.SearchRegistry(context.TODO(), sc, registry, term, limit) - if err != nil { - logrus.Errorf("error searching registry %q: %v", registry, err) - return []searchParams{}, nil - } - 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 opts.limit != 0 && opts.limit < len(results) { - limit = opts.limit - } - - paramsArr := []searchParams{} - for i := 0; i < limit; i++ { - if len(opts.filter) > 0 { - // Check whether query matches filters - if !(matchesAutomatedFilter(filter, results[i]) && matchesOfficialFilter(filter, results[i]) && matchesStarFilter(filter, 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 && !opts.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 := searchParams{ - Index: index, - Name: name, - Description: description, - Official: official, - Automated: automated, - Stars: results[i].StarCount, - } - paramsArr = append(paramsArr, params) - } - return paramsArr, nil -} - -func generateSearchOutput(term string, registries []string, opts searchOpts, filter searchFilterParams) error { - // searchOutputData is used as a return value for searching in parallel. - type searchOutputData struct { - data []searchParams - err error - } - - // Let's follow Firefox by limiting parallel downloads to 6. - sem := semaphore.NewWeighted(6) - wg := sync.WaitGroup{} - wg.Add(len(registries)) - data := make([]searchOutputData, len(registries)) - - getSearchOutputHelper := func(index int, registry string) { - defer sem.Release(1) - defer wg.Done() - searchOutput, err := getSearchOutput(term, registry, opts, filter) - data[index] = searchOutputData{data: searchOutput, err: err} - } - - ctx := context.Background() - for i := range registries { - sem.Acquire(ctx, 1) - go getSearchOutputHelper(i, registries[i]) - } - - wg.Wait() - searchOutput := []searchParams{} - for _, d := range data { - if d.err != nil { - return d.err - } - searchOutput = append(searchOutput, d.data...) - } - if len(searchOutput) == 0 { - return nil - } - out := formats.StdoutTemplateArray{Output: searchToGeneric(searchOutput), Template: opts.format, Fields: searchOutput[0].headerMap()} - return formats.Writer(out).Out() -} - -func parseSearchFilter(opts *searchOpts) (*searchFilterParams, error) { - filterParams := &searchFilterParams{} - ptrTrue := true - ptrFalse := false - for _, filter := range opts.filter { - arr := strings.Split(filter, "=") - 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") - } - filterParams.stars = stars - break - case "is-automated": - if len(arr) == 2 && arr[1] == "false" { - filterParams.isAutomated = &ptrFalse - } else { - filterParams.isAutomated = &ptrTrue - } - break - case "is-official": - if len(arr) == 2 && arr[1] == "false" { - filterParams.isOfficial = &ptrFalse - } else { - filterParams.isOfficial = &ptrTrue - } - break - default: - return nil, errors.Errorf("invalid filter type %q", filter) - } - } - return filterParams, nil -} - -func matchesStarFilter(filter searchFilterParams, result docker.SearchResult) bool { - return result.StarCount >= filter.stars -} - -func matchesAutomatedFilter(filter searchFilterParams, result docker.SearchResult) bool { - if filter.isAutomated != nil { - return result.IsAutomated == *filter.isAutomated - } - return true -} - -func matchesOfficialFilter(filter searchFilterParams, result docker.SearchResult) bool { - if filter.isOfficial != nil { - return result.IsOfficial == *filter.isOfficial - } - return true -} |