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>master
							parent
							
								
									d5441ca506
								
							
						
					
					
						commit
						9e211edc9d
					
				|  | @ -10,6 +10,7 @@ import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/docker/distribution" | 	"github.com/docker/distribution" | ||||||
|  | @ -213,28 +214,35 @@ func (t *tags) All(ctx context.Context) ([]string, error) { | ||||||
| 		return tags, err | 		return tags, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resp, err := t.client.Get(u) | 	for { | ||||||
| 	if err != nil { | 		resp, err := t.client.Get(u) | ||||||
| 		return tags, err |  | ||||||
| 	} |  | ||||||
| 	defer resp.Body.Close() |  | ||||||
| 
 |  | ||||||
| 	if SuccessStatus(resp.StatusCode) { |  | ||||||
| 		b, err := ioutil.ReadAll(resp.Body) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return tags, err | 			return tags, err | ||||||
| 		} | 		} | ||||||
|  | 		defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 		tagsResponse := struct { | 		if SuccessStatus(resp.StatusCode) { | ||||||
| 			Tags []string `json:"tags"` | 			b, err := ioutil.ReadAll(resp.Body) | ||||||
| 		}{} | 			if err != nil { | ||||||
| 		if err := json.Unmarshal(b, &tagsResponse); err != nil { | 				return tags, err | ||||||
| 			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) { | func descriptorFromResponse(response *http.Response) (distribution.Descriptor, error) { | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ package client | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"log" | 	"log" | ||||||
|  | @ -949,6 +950,78 @@ func TestManifestTags(t *testing.T) { | ||||||
| 	// TODO(dmcgowan): Check for error cases
 | 	// 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) { | func TestManifestUnauthorized(t *testing.T) { | ||||||
| 	repo, _ := reference.ParseNamed("test.example.com/repo") | 	repo, _ := reference.ParseNamed("test.example.com/repo") | ||||||
| 	_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | 	_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue