diff --git a/apis/server/image_bridge.go b/apis/server/image_bridge.go index b821a7b5f..f4162effe 100644 --- a/apis/server/image_bridge.go +++ b/apis/server/image_bridge.go @@ -93,7 +93,17 @@ func (s *Server) searchImages(ctx context.Context, rw http.ResponseWriter, req * searchPattern := req.FormValue("term") registry := req.FormValue("registry") - searchResultItem, err := s.ImageMgr.SearchImages(ctx, searchPattern, registry) + // get registry auth from Request header + authStr := req.Header.Get("X-Registry-Auth") + authConfig := types.AuthConfig{} + if authStr != "" { + data := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authStr)) + if err := json.NewDecoder(data).Decode(&authConfig); err != nil { + return err + } + } + + searchResultItem, err := s.ImageMgr.SearchImages(ctx, searchPattern, registry, &authConfig) if err != nil { logrus.Errorf("failed to search images from registry: %v", err) return err diff --git a/apis/swagger.yml b/apis/swagger.yml index 15a41c3df..371a4a523 100644 --- a/apis/swagger.yml +++ b/apis/swagger.yml @@ -380,19 +380,7 @@ paths: in: "query" description: "Search images from specified registry" type: "string" - - - name: "limit" - in: "query" - description: "Maximum number of results to return" - type: "integer" - - name: "filters" - in: "query" - description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: - - `is-automated=(true|false)` - - `is-official=(true|false)` - - `stars=` Matches images that has at least 'number' stars. - type: "string" + # TODO: add limit and filters /images/{imageid}/tag: post: diff --git a/cli/search.go b/cli/search.go index e3e7c15ef..c88aa040d 100644 --- a/cli/search.go +++ b/cli/search.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/alibaba/pouch/registry" + "github.com/spf13/cobra" ) @@ -15,7 +17,7 @@ type SearchCommand struct { registry string } -// Init initialize start command. +// Init initialize search command. func (s *SearchCommand) Init(c *Cli) { s.cli = c @@ -44,7 +46,11 @@ func (s *SearchCommand) runSearch(args []string) error { apiClient := s.cli.Client() term := args[0] - searchResults, err := apiClient.ImageSearch(ctx, term, s.registry) + if len(s.registry) == 0 { + s.registry = registry.DefaultRegistry + } + + searchResults, err := apiClient.ImageSearch(ctx, term, s.registry, fetchRegistryAuth(s.registry)) if err != nil { return err @@ -61,7 +67,6 @@ func (s *SearchCommand) runSearch(args []string) error { return nil } -// chang bool value to ok or "" bool => "[OK]" false => "" func boolToOKOrNot(isTrue bool) string { if isTrue { return "[OK]" diff --git a/client/image_search.go b/client/image_search.go index a6a4e75d8..df184809a 100644 --- a/client/image_search.go +++ b/client/image_search.go @@ -9,17 +9,17 @@ import ( ) // ImageSearch requests daemon to search an image from registry. -func (client *APIClient) ImageSearch(ctx context.Context, term string, register string) ([]types.SearchResultItem, error) { +func (client *APIClient) ImageSearch(ctx context.Context, term, registry, encodedAuth string) ([]types.SearchResultItem, error) { var results []types.SearchResultItem q := url.Values{} q.Set("term", term) - if len(register) > 0 { - q.Set("registry", register) - } + q.Set("registry", registry) - // todo: add some auth info headers := map[string][]string{} + if encodedAuth != "" { + headers["X-Registry-Auth"] = []string{encodedAuth} + } resp, err := client.post(ctx, "/images/search", q, nil, headers) diff --git a/client/image_search_test.go b/client/image_search_test.go index b7f48c9c6..39677a353 100644 --- a/client/image_search_test.go +++ b/client/image_search_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/alibaba/pouch/apis/types" + "github.com/stretchr/testify/assert" ) @@ -18,8 +19,8 @@ func TestImageSearchServerError(t *testing.T) { client := &APIClient{ HTTPCli: newMockClient(errorMockResponse(http.StatusInternalServerError, "Server error")), } - term, registry := "", "nginx" - _, err := client.ImageSearch(context.Background(), term, registry) + term, registry, auth := "", "nginx", "" + _, err := client.ImageSearch(context.Background(), term, registry, auth) if err == nil || !strings.Contains(err.Error(), "Server error") { t.Fatalf("expected a Server Error, got %v", err) } @@ -59,7 +60,7 @@ func TestImageSearchOK(t *testing.T) { HTTPCli: httpClient, } - searchResults, err := client.ImageSearch(context.Background(), "nginx", "") + searchResults, err := client.ImageSearch(context.Background(), "nginx", "", "") if err != nil { t.Fatal(err) } @@ -67,3 +68,14 @@ func TestImageSearchOK(t *testing.T) { assert.Equal(t, searchResults[0].StarCount, int64(1233)) assert.Equal(t, searchResults[0].Name, "nginx") } + +func TestImageSearchStatusUnauthorizedError(t *testing.T) { + client := &APIClient{ + HTTPCli: newMockClient(errorMockResponse(http.StatusUnauthorized, "Unauthorized Error")), + } + term, registry, auth := "", "nginx", "some-auth-code" + _, err := client.ImageSearch(context.Background(), term, registry, auth) + if err == nil || !strings.Contains(err.Error(), "Unauthorized Error") { + t.Fatalf("expected a Unauthorized Error, got %v", err) + } +} diff --git a/client/interface.go b/client/interface.go index 785666ff3..7301b5b67 100644 --- a/client/interface.go +++ b/client/interface.go @@ -59,7 +59,7 @@ type ImageAPIClient interface { ImageSave(ctx context.Context, imageName string) (io.ReadCloser, error) ImageHistory(ctx context.Context, name string) ([]types.HistoryResultItem, error) ImagePush(ctx context.Context, ref, encodedAuth string) (io.ReadCloser, error) - ImageSearch(ctx context.Context, term string, registry string) ([]types.SearchResultItem, error) + ImageSearch(ctx context.Context, term, registry, encodedAuth string) ([]types.SearchResultItem, error) } // VolumeAPIClient defines methods of Volume client. diff --git a/daemon/mgr/image.go b/daemon/mgr/image.go index dc6eee8d4..7c5a0f941 100644 --- a/daemon/mgr/image.go +++ b/daemon/mgr/image.go @@ -23,6 +23,7 @@ import ( "github.com/alibaba/pouch/pkg/reference" "github.com/alibaba/pouch/pkg/utils" + registry2 "github.com/alibaba/pouch/registry" "github.com/containerd/containerd" "github.com/containerd/containerd/content" ctrdmetaimages "github.com/containerd/containerd/images" @@ -61,7 +62,7 @@ type ImageMgr interface { ListImages(ctx context.Context, filter filters.Args) ([]types.ImageInfo, error) // Search Images from specified registry. - SearchImages(ctx context.Context, name string, registry string) ([]types.SearchResultItem, error) + SearchImages(ctx context.Context, name, registry string, authConfig *types.AuthConfig) ([]types.SearchResultItem, error) // RemoveImage deletes an image by reference. RemoveImage(ctx context.Context, idOrRef string, force bool) error @@ -327,20 +328,30 @@ func (mgr *ImageManager) ListImages(ctx context.Context, filter filters.Args) ([ } // SearchImages searches imaged from specified registry. -func (mgr *ImageManager) SearchImages(ctx context.Context, name string, registry string) ([]types.SearchResultItem, error) { +func (mgr *ImageManager) SearchImages(ctx context.Context, name, registry string, auth *types.AuthConfig) ([]types.SearchResultItem, error) { // Directly send API calls towards specified registry if len(registry) == 0 { - registry = "https://index.docker.io/v1/" + registry = registry2.DefaultRegistry } u := registry + "search?q=" + url.QueryEscape(name) - res, err := http.Get(u) + req, err := http.NewRequest("GET", u, nil) + if err != nil { + return nil, err + } + + if auth != nil && auth.IdentityToken != "" && auth.Username != "" { + req.SetBasicAuth(auth.Username, auth.Password) + } + + res, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer res.Body.Close() + if res.StatusCode != 200 { - return nil, errtypes.ErrTimeout + return nil, fmt.Errorf("Unexepected status code %d", res.StatusCode) } rawData, err := ioutil.ReadAll(res.Body) @@ -348,7 +359,6 @@ func (mgr *ImageManager) SearchImages(ctx context.Context, name string, registry return nil, err } - // todo: to move where it should be type SearchResults struct { Query string `json:"query"` NumResults int `json:"num_results"` @@ -360,9 +370,6 @@ func (mgr *ImageManager) SearchImages(ctx context.Context, name string, registry return searchResults.Results, err - // todo: whether this code rebuild in ctrd ? - // todo: to add some session code and log info? - //return mgr.client.SearchImage(ctx, name, registry) } // RemoveImage deletes a reference. diff --git a/daemon/mgr/system.go b/daemon/mgr/system.go index 807e32399..43415d932 100644 --- a/daemon/mgr/system.go +++ b/daemon/mgr/system.go @@ -152,7 +152,7 @@ func (mgr *SystemManager) Info() (types.SystemInfo, error) { // ID: , CgroupDriver: mgr.config.GetCgroupDriver(), Images: int64(len(images)), - IndexServerAddress: "https://index.docker.io/v1/", + IndexServerAddress: registry.DefaultRegistry, DefaultRegistry: mgr.config.DefaultRegistry, KernelVersion: kernelVersion, Labels: mgr.config.Labels, diff --git a/registry/config.go b/registry/config.go index 9694204a3..e532a5228 100644 --- a/registry/config.go +++ b/registry/config.go @@ -2,4 +2,7 @@ package registry var ( defaultV2Registry = "registry-1.docker.io" + + // DefaultRegistry set default registry value + DefaultRegistry = "https://index.docker.io/v1/" ) diff --git a/test/api_image_search_test.go b/test/api_image_search_test.go index 9601f3549..387873926 100644 --- a/test/api_image_search_test.go +++ b/test/api_image_search_test.go @@ -1,13 +1,13 @@ package main import ( - "github.com/alibaba/pouch/test/environment" - "net/url" "github.com/alibaba/pouch/apis/types" + "github.com/alibaba/pouch/test/environment" "github.com/alibaba/pouch/test/request" "github.com/alibaba/pouch/test/util" + "github.com/go-check/check" ) diff --git a/test/cli_search_test.go b/test/cli_search_test.go index d605d4ea1..d75f32550 100644 --- a/test/cli_search_test.go +++ b/test/cli_search_test.go @@ -25,5 +25,5 @@ func (suite *PouchSearchSuite) TestSearchWorks(c *check.C) { // TestSearchInSpecificRegistry test search in specific registry func (suite *PouchSearchSuite) TestSearchInSpecificRegistry(c *check.C) { - // TODO: Verification specific registry? + // TODO: Verification specific registry }