1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #23107 from yongtang/23055-docker-search-limit

Add `--limit` option to `docker search`
This commit is contained in:
Sebastiaan van Stijn 2016-06-03 14:25:42 +02:00
commit 020a86b3d9
13 changed files with 79 additions and 14 deletions

View file

@ -34,6 +34,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
cmd := Cli.Subcmd("search", []string{"TERM"}, Cli.DockerCommands["search"].Description, true)
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output")
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
flLimit := cmd.Int([]string{"-limit"}, registry.DefaultSearchLimit, "Max number of search results")
// Deprecated since Docker 1.12 in favor of "--filter"
automated := cmd.Bool([]string{"#-automated"}, false, "Only show automated builds - DEPRECATED")
@ -72,6 +73,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
Filters: filterArgs,
Limit: *flLimit,
}
unorderedResults, err := cli.client.ImageSearch(ctx, name, options)

View file

@ -39,5 +39,5 @@ type importExportBackend interface {
type registryBackend interface {
PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
}

View file

@ -6,12 +6,14 @@ import (
"fmt"
"io"
"net/http"
"strconv"
"strings"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/container"
"github.com/docker/engine-api/types/versions"
@ -301,7 +303,15 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
headers[k] = v
}
}
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), config, headers)
limit := registry.DefaultSearchLimit
if r.Form.Get("limit") != "" {
limitValue, err := strconv.Atoi(r.Form.Get("limit"))
if err != nil {
return err
}
limit = limitValue
}
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
if err != nil {
return err
}

View file

@ -20,7 +20,7 @@ var acceptedSearchFilterTags = map[string]bool{
// SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login.
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int,
authConfig *types.AuthConfig,
headers map[string][]string) (*registrytypes.SearchResults, error) {
@ -61,7 +61,7 @@ func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs s
}
}
unfilteredResult, err := daemon.RegistryService.Search(ctx, term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
unfilteredResult, err := daemon.RegistryService.Search(ctx, term, limit, authConfig, dockerversion.DockerUserAgent(ctx), headers)
if err != nil {
return nil, err
}

View file

@ -21,7 +21,7 @@ type FakeService struct {
results []registrytypes.SearchResult
}
func (s *FakeService) Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
func (s *FakeService) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
if s.shouldReturnError {
return nil, fmt.Errorf("Search unknown error")
}
@ -81,7 +81,7 @@ func TestSearchRegistryForImagesErrors(t *testing.T) {
shouldReturnError: e.shouldReturnError,
},
}
_, err := daemon.SearchRegistryForImages(context.Background(), e.filtersArgs, "term", nil, map[string][]string{})
_, err := daemon.SearchRegistryForImages(context.Background(), e.filtersArgs, "term", 25, nil, map[string][]string{})
if err == nil {
t.Errorf("%d: expected an error, got nothing", index)
}
@ -328,7 +328,7 @@ func TestSearchRegistryForImages(t *testing.T) {
results: s.registryResults,
},
}
results, err := daemon.SearchRegistryForImages(context.Background(), s.filtersArgs, term, nil, map[string][]string{})
results, err := daemon.SearchRegistryForImages(context.Background(), s.filtersArgs, term, 25, nil, map[string][]string{})
if err != nil {
t.Errorf("%d: %v", index, err)
}

View file

@ -124,6 +124,7 @@ This section lists each version from latest to oldest. Each listing includes a
* `GET /images/json` now supports filters `since` and `before`.
* `POST /containers/(id or name)/start` no longer accepts a `HostConfig`.
* `POST /images/(name)/tag` no longer has a `force` query parameter.
* `GET /images/search` now supports maximum returned search results `limit`.
### v1.23 API changes

View file

@ -2130,6 +2130,7 @@ Search for an image on [Docker Hub](https://hub.docker.com).
Query Parameters:
- **term** term to search
- **limit** maximum returned search results
- **filters** a JSON encoded value of the filters (a map[string][]string) to process on the images list. Available filters:
- `stars=<number>`
- `is-automated=(true|false)`

View file

@ -19,6 +19,7 @@ parent = "smn_cli"
- is-official=(true|false)
- stars=<number> - image has at least 'number' stars
--help Print usage
--limit=25 Maximum returned search results
--no-trunc Don't truncate output
Search [Docker Hub](https://hub.docker.com) for images
@ -74,6 +75,12 @@ at least 3 stars and the description isn't truncated in the output:
progrium/busybox 50 [OK]
radial/busyboxplus Full-chain, Internet enabled, busybox made from scratch. Comes in git and cURL flavors. 8 [OK]
## Limit search results (--limit)
The flag `--limit` is the maximium number of results returned by a search. This value could
be in the range between 1 and 100. The default value of `--limit` is 25.
## Filtering
The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there is more

View file

@ -1,6 +1,7 @@
package main
import (
"fmt"
"strings"
"github.com/docker/docker/pkg/integration/checker"
@ -97,3 +98,34 @@ func (s *DockerSuite) TestSearchOnCentralRegistryWithDash(c *check.C) {
dockerCmd(c, "search", "ubuntu-")
}
// test case for #23055
func (s *DockerSuite) TestSearchWithLimit(c *check.C) {
testRequires(c, Network, DaemonIsLinux)
limit := 10
out, _, err := dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
c.Assert(err, checker.IsNil)
outSlice := strings.Split(out, "\n")
c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
limit = 50
out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
c.Assert(err, checker.IsNil)
outSlice = strings.Split(out, "\n")
c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
limit = 100
out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
c.Assert(err, checker.IsNil)
outSlice = strings.Split(out, "\n")
c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
limit = 0
out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
c.Assert(err, checker.Not(checker.IsNil))
limit = 200
out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
c.Assert(err, checker.Not(checker.IsNil))
}

View file

@ -8,6 +8,7 @@ docker-search - Search the Docker Hub for images
**docker search**
[**-f**|**--filter**[=*[]*]]
[**--help**]
[**--limit**[=*LIMIT*]]
[**--no-trunc**]
TERM
@ -30,6 +31,9 @@ of stars awarded, whether the image is official, and whether it is automated.
**--help**
Print usage statement
**--limit**=*LIMIT*
Maximum returned search results. The default is 25.
**--no-trunc**=*true*|*false*
Don't truncate output. The default is *false*.

View file

@ -730,7 +730,7 @@ func TestPushImageJSONIndex(t *testing.T) {
func TestSearchRepositories(t *testing.T) {
r := spawnTestRegistrySession(t)
results, err := r.SearchRepositories("fakequery")
results, err := r.SearchRepositories("fakequery", 25)
if err != nil {
t.Fatal(err)
}

View file

@ -15,6 +15,11 @@ import (
registrytypes "github.com/docker/engine-api/types/registry"
)
const (
// DefaultSearchLimit is the default value for maximum number of returned search results.
DefaultSearchLimit = 25
)
// Service is the interface defining what a registry service should implement.
type Service interface {
Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error)
@ -22,7 +27,7 @@ type Service interface {
LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error)
ResolveRepository(name reference.Named) (*RepositoryInfo, error)
ResolveIndex(name string) (*registrytypes.IndexInfo, error)
Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
ServiceConfig() *registrytypes.ServiceConfig
TLSConfig(hostname string) (*tls.Config, error)
}
@ -108,7 +113,7 @@ func splitReposSearchTerm(reposName string) (string, string) {
// Search queries the public registry for images matching the specified
// search terms, and returns the results.
func (s *DefaultService) Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
func (s *DefaultService) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
// TODO Use ctx when searching for repositories
if err := validateNoScheme(term); err != nil {
return nil, err
@ -139,9 +144,9 @@ func (s *DefaultService) Search(ctx context.Context, term string, authConfig *ty
localName = strings.SplitN(localName, "/", 2)[1]
}
return r.SearchRepositories(localName)
return r.SearchRepositories(localName, limit)
}
return r.SearchRepositories(remoteName)
return r.SearchRepositories(remoteName, limit)
}
// ResolveRepository splits a repository name into its components

View file

@ -721,9 +721,12 @@ func shouldRedirect(response *http.Response) bool {
}
// SearchRepositories performs a search against the remote repository
func (r *Session) SearchRepositories(term string) (*registrytypes.SearchResults, error) {
func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.SearchResults, error) {
if limit < 1 || limit > 100 {
return nil, fmt.Errorf("Limit %d is outside the range of [1, 100]", limit)
}
logrus.Debugf("Index server: %s", r.indexEndpoint)
u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term)
u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit))
req, err := http.NewRequest("GET", u, nil)
if err != nil {