From 722a143ce9dc8c1604ff388d2ed94c38f6d9f0c1 Mon Sep 17 00:00:00 2001 From: Carlos Panato Date: Mon, 22 Feb 2021 14:45:23 +0100 Subject: [PATCH] implement catalog tests Signed-off-by: Carlos Panato --- pkg/crane/crane_test.go | 15 +++++++++- pkg/registry/manifest.go | 56 +++++++++++++++++++++++++++++++++++ pkg/registry/registry.go | 3 ++ pkg/registry/registry_test.go | 7 +++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/pkg/crane/crane_test.go b/pkg/crane/crane_test.go index 5e402c19cd..80a257af73 100644 --- a/pkg/crane/crane_test.go +++ b/pkg/crane/crane_test.go @@ -34,7 +34,6 @@ import ( "github.com/google/go-containerregistry/pkg/v1/remote" ) -// TODO(jonjohnsonjr): Test crane.Catalog behavior. // TODO(jonjohnsonjr): Test crane.Copy failures. func TestCraneRegistry(t *testing.T) { // Set up a fake registry. @@ -195,6 +194,20 @@ func TestCraneRegistry(t *testing.T) { if err := compare.Images(dstPulled, copied); err != nil { t.Fatal(err) } + + // List Catalog + repos, err := crane.Catalog(u.Host) + if err != nil { + t.Fatal(err) + } + if len(repos) != 2 { + t.Fatalf("wanted 2 repos, got %d", len(repos)) + } + + _, err = crane.Catalog("registry.honk") + if err == nil { + t.Fatal("should error, registry does not exist") + } } func TestCraneCopyIndex(t *testing.T) { diff --git a/pkg/registry/manifest.go b/pkg/registry/manifest.go index 5dc920abb7..cf58724444 100644 --- a/pkg/registry/manifest.go +++ b/pkg/registry/manifest.go @@ -31,6 +31,10 @@ import ( "github.com/google/go-containerregistry/pkg/v1/types" ) +type catalog struct { + Repos []string `json:"repositories"` +} + type listTags struct { Name string `json:"name"` Tags []string `json:"tags"` @@ -65,6 +69,16 @@ func isTags(req *http.Request) bool { return elems[len(elems)-2] == "tags" } +func isCatalog(req *http.Request) bool { + elems := strings.Split(req.URL.Path, "/") + elems = elems[1:] + if len(elems) < 2 { + return false + } + + return elems[len(elems)-1] == "_catalog" +} + // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pulling-an-image-manifest // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pushing-an-image func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regError { @@ -273,3 +287,45 @@ func (m *manifests) handleTags(resp http.ResponseWriter, req *http.Request) *reg Message: "We don't understand your method + url", } } + +func (m *manifests) handleCatalog(resp http.ResponseWriter, req *http.Request) *regError { + query := req.URL.Query() + nStr := query.Get("n") + n := 10000 + if nStr != "" { + n, _ = strconv.Atoi(nStr) + } + + if req.Method == "GET" { + m.lock.Lock() + defer m.lock.Unlock() + + var repos []string + countRepos := 0 + // TODO: implement pagination + for key := range m.manifests { + if countRepos >= n { + break + } + countRepos++ + + repos = append(repos, key) + } + + repositoriesToList := catalog{ + Repos: repos, + } + + msg, _ := json.Marshal(repositoriesToList) + resp.Header().Set("Content-Length", fmt.Sprint(len(msg))) + resp.WriteHeader(http.StatusOK) + io.Copy(resp, bytes.NewReader([]byte(msg))) + return nil + } + + return ®Error{ + Status: http.StatusBadRequest, + Code: "METHOD_UNKNOWN", + Message: "We don't understand your method + url", + } +} diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 8e92bfb2c5..f28f219a75 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -47,6 +47,9 @@ func (r *registry) v2(resp http.ResponseWriter, req *http.Request) *regError { if isTags(req) { return r.manifests.handleTags(resp, req) } + if isCatalog(req) { + return r.manifests.handleCatalog(resp, req) + } resp.Header().Set("Docker-Distribution-API-Version", "registry/2.0") if req.URL.Path != "/v2/" && req.URL.Path != "/v2" { return ®Error{ diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index debf0117b3..af9a384db3 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -390,6 +390,13 @@ func TestCalls(t *testing.T) { URL: "/v2/foo/tags/list?n=1000", Code: http.StatusNotFound, }, + { + Description: "list repos", + Manifests: map[string]string{"foo/manifests/latest": "foo", "bar/manifests/latest": "bar"}, + Method: "GET", + URL: "/v2/_catalog?n=1000", + Code: http.StatusOK, + }, } for _, tc := range tcs {