Implement paging support for tags and repositories. Closes #1.

parent be7b1ba5
Pipeline #103499995 failed with stage
in 29 seconds
......@@ -7,6 +7,7 @@ require (
github.com/rs/zerolog v1.11.0
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
)
go 1.13
......@@ -9,12 +9,15 @@ import (
"strings"
"github.com/pkg/errors"
"github.com/tomnomnom/linkheader"
"gitlab.com/zerok/container-inventory/pkg/sessionfile"
)
var ErrUnauthorized = errors.New("unauthorized")
var ErrForbidden = errors.New("forbidden")
const pageSize = 50
type Client struct {
Host string
session *sessionfile.Session
......@@ -83,28 +86,46 @@ func (c *Client) buildURL(path string, args ...interface{}) string {
func (c *Client) ListRepositories(ctx context.Context) ([]string, error) {
hc := http.Client{}
req, _ := http.NewRequest(http.MethodGet, c.buildURL("/v2/_catalog"), nil)
c.injectSession(req)
resp, err := hc.Do(req.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "failed to fetch repositories")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusForbidden:
return nil, ErrForbidden
case http.StatusUnauthorized:
return nil, ErrUnauthorized
default:
return nil, errors.Errorf("Unexpected status code: %d", resp.StatusCode)
result := make([]string, 0, 10)
next := c.buildURL("/v2/_catalog?n=%d", pageSize)
for {
if next == "" {
break
}
req, _ := http.NewRequest(http.MethodGet, next, nil)
c.injectSession(req)
resp, err := hc.Do(req.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "failed to fetch repositories")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusForbidden:
return nil, ErrForbidden
case http.StatusUnauthorized:
return nil, ErrUnauthorized
default:
return nil, errors.Errorf("Unexpected status code: %d", resp.StatusCode)
}
}
var lr listRepositoriesResponse
if err := json.NewDecoder(resp.Body).Decode(&lr); err != nil {
return nil, errors.Wrap(err, "failed to decode repositories response")
}
result = append(result, lr.Repositories...)
next = getNext(resp)
}
var lr listRepositoriesResponse
if err := json.NewDecoder(resp.Body).Decode(&lr); err != nil {
return nil, errors.Wrap(err, "failed to decode repositories response")
return result, nil
}
func getNext(resp *http.Response) string {
if links := linkheader.Parse(resp.Header.Get("Link")).FilterByRel("next"); links != nil {
for _, link := range links {
return link.URL
}
}
return lr.Repositories, nil
return ""
}
func (c *Client) GetManifest(ctx context.Context, image string, ref string) (*Manifest, error) {
......@@ -136,26 +157,37 @@ func (c *Client) GetManifest(ctx context.Context, image string, ref string) (*Ma
func (c *Client) ListTags(ctx context.Context, image string) ([]string, error) {
hc := http.Client{}
req, _ := http.NewRequest(http.MethodGet, c.buildURL("/v2/%s/tags/list", url.QueryEscape(image)), nil)
c.injectSession(req)
resp, err := hc.Do(req.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "failed to fetch repositories")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusForbidden:
return nil, ErrForbidden
case http.StatusUnauthorized:
return nil, ErrUnauthorized
default:
return nil, errors.Errorf("Unexpected status code: %d", resp.StatusCode)
result := make([]string, 0, 10)
query := url.Values{}
query.Set("n", fmt.Sprintf("%d", pageSize))
next := c.buildURL("/v2/%s/tags/list?%s", url.QueryEscape(image), query.Encode())
for {
if next == "" {
break
}
req, _ := http.NewRequest(http.MethodGet, next, nil)
c.injectSession(req)
resp, err := hc.Do(req.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "failed to fetch repositories")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusForbidden:
return nil, ErrForbidden
case http.StatusUnauthorized:
return nil, ErrUnauthorized
default:
return nil, errors.Errorf("Unexpected status code: %d", resp.StatusCode)
}
}
var lr listTagsResponse
if err := json.NewDecoder(resp.Body).Decode(&lr); err != nil {
return nil, errors.Wrap(err, "failed to decode repositories response")
}
result = append(result, lr.Tags...)
next = getNext(resp)
}
var lr listTagsResponse
if err := json.NewDecoder(resp.Body).Decode(&lr); err != nil {
return nil, errors.Wrap(err, "failed to decode repositories response")
}
return lr.Tags, nil
return result, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment