Changes the client Tags All() method to follow links
This returns all tags even when the registry forces pagination. Signed-off-by: Brian Bland <brian.t.bland@gmail.com>
This commit is contained in:
parent
5f7f871d8f
commit
a1f9f71e67
2 changed files with 97 additions and 16 deletions
|
@ -10,6 +10,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution"
|
||||
|
@ -213,28 +214,35 @@ func (t *tags) All(ctx context.Context) ([]string, error) {
|
|||
return tags, err
|
||||
}
|
||||
|
||||
resp, err := t.client.Get(u)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if SuccessStatus(resp.StatusCode) {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
for {
|
||||
resp, err := t.client.Get(u)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tagsResponse := struct {
|
||||
Tags []string `json:"tags"`
|
||||
}{}
|
||||
if err := json.Unmarshal(b, &tagsResponse); err != nil {
|
||||
return tags, err
|
||||
if SuccessStatus(resp.StatusCode) {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return tags, err
|
||||
}
|
||||
|
||||
tagsResponse := struct {
|
||||
Tags []string `json:"tags"`
|
||||
}{}
|
||||
if err := json.Unmarshal(b, &tagsResponse); err != nil {
|
||||
return tags, err
|
||||
}
|
||||
tags = append(tags, tagsResponse.Tags...)
|
||||
if link := resp.Header.Get("Link"); link != "" {
|
||||
u = strings.Trim(strings.Split(link, ";")[0], "<>")
|
||||
} else {
|
||||
return tags, nil
|
||||
}
|
||||
} else {
|
||||
return tags, HandleErrorResponse(resp)
|
||||
}
|
||||
tags = tagsResponse.Tags
|
||||
return tags, nil
|
||||
}
|
||||
return tags, HandleErrorResponse(resp)
|
||||
}
|
||||
|
||||
func descriptorFromResponse(response *http.Response) (distribution.Descriptor, error) {
|
||||
|
|
|
@ -3,6 +3,7 @@ package client
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -949,6 +950,78 @@ func TestManifestTags(t *testing.T) {
|
|||
// TODO(dmcgowan): Check for error cases
|
||||
}
|
||||
|
||||
func TestManifestTagsPaginated(t *testing.T) {
|
||||
s := httptest.NewServer(http.NotFoundHandler())
|
||||
defer s.Close()
|
||||
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo/tags/list")
|
||||
tagsList := []string{"tag1", "tag2", "funtag"}
|
||||
var m testutil.RequestResponseMap
|
||||
for i := 0; i < 3; i++ {
|
||||
body, err := json.Marshal(map[string]interface{}{
|
||||
"name": "test.example.com/repo/tags/list",
|
||||
"tags": []string{tagsList[i]},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
queryParams := make(map[string][]string)
|
||||
if i > 0 {
|
||||
queryParams["n"] = []string{"1"}
|
||||
queryParams["last"] = []string{tagsList[i-1]}
|
||||
}
|
||||
headers := http.Header(map[string][]string{
|
||||
"Content-Length": {fmt.Sprint(len(body))},
|
||||
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
|
||||
})
|
||||
if i < 2 {
|
||||
headers.Set("Link", "<"+s.URL+"/v2/"+repo.Name()+"/tags/list?n=1&last="+tagsList[i]+`>; rel="next"`)
|
||||
}
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "GET",
|
||||
Route: "/v2/" + repo.Name() + "/tags/list",
|
||||
QueryParams: queryParams,
|
||||
},
|
||||
Response: testutil.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: body,
|
||||
Headers: headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
s.Config.Handler = testutil.NewHandler(m)
|
||||
|
||||
r, err := NewRepository(context.Background(), repo, s.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
tagService := r.Tags(ctx)
|
||||
|
||||
tags, err := tagService.All(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(tags, err)
|
||||
}
|
||||
if len(tags) != 3 {
|
||||
t.Fatalf("Wrong number of tags returned: %d, expected 3", len(tags))
|
||||
}
|
||||
|
||||
expected := map[string]struct{}{
|
||||
"tag1": {},
|
||||
"tag2": {},
|
||||
"funtag": {},
|
||||
}
|
||||
for _, t := range tags {
|
||||
delete(expected, t)
|
||||
}
|
||||
if len(expected) != 0 {
|
||||
t.Fatalf("unexpected tags returned: %v", expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestUnauthorized(t *testing.T) {
|
||||
repo, _ := reference.ParseNamed("test.example.com/repo")
|
||||
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
|
|
Loading…
Reference in a new issue