Skip to content

Commit 5468963

Browse files
feat: enable referrers pagination (#56)
Signed-off-by: wangxiaoxuan273 <wangxiaoxuan119@gmail.com>
1 parent f27b092 commit 5468963

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

registry/handlers/referrers.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package handlers
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
8+
"net/url"
79
"path"
10+
"strconv"
811

912
"github.com/distribution/distribution/v3"
1013
dcontext "github.com/distribution/distribution/v3/context"
@@ -20,6 +23,12 @@ import (
2023
v1 "github.com/opencontainers/image-spec/specs-go/v1"
2124
)
2225

26+
const (
27+
pageSizeMin = 1
28+
pageSizeDefault = 10
29+
pageSizeMax = 100
30+
)
31+
2332
// referrersDispatcher takes the request context and builds the
2433
// appropriate handler for handling referrers requests.
2534
func referrersDispatcher(ctx *Context, r *http.Request) http.Handler {
@@ -59,13 +68,30 @@ func (h *referrersHandler) GetReferrers(w http.ResponseWriter, r *http.Request)
5968
return
6069
}
6170

71+
// extract the artifactType filter.
6272
var annotations map[string]string
6373
var artifactTypeFilter string
6474
if artifactTypeFilter = r.URL.Query().Get("artifactType"); artifactTypeFilter != "" {
6575
annotations = map[string]string{
6676
v1.AnnotationReferrersFiltersApplied: "artifactType",
6777
}
6878
}
79+
80+
// extract the page size info. Users define page size by using the n
81+
// query parameter. The default page size is 10.
82+
pageSize, err := strconv.Atoi(r.URL.Query().Get("n"))
83+
if err != nil || pageSize < pageSizeMin || pageSize > pageSizeMax {
84+
pageSize = pageSizeDefault
85+
}
86+
87+
// extract the page number info.
88+
pageNumber, err := strconv.Atoi(r.URL.Query().Get("p"))
89+
if err != nil || pageNumber < 0 {
90+
pageNumber = 0
91+
}
92+
93+
// currently, pagination would call generateReferrersList multiple times
94+
// and this is not efficient. This implementation is for testing purpose.
6995
referrers, err := h.generateReferrersList(h, h.Digest, artifactTypeFilter)
7096
if err != nil {
7197
if _, ok := err.(distribution.ErrManifestUnknownRevision); ok {
@@ -76,8 +102,16 @@ func (h *referrersHandler) GetReferrers(w http.ResponseWriter, r *http.Request)
76102
return
77103
}
78104

79-
if referrers == nil {
105+
startIndex := pageNumber * pageSize
106+
107+
if referrers == nil || startIndex >= len(referrers) {
80108
referrers = []v1.Descriptor{}
109+
} else if len(referrers)-startIndex <= pageSize {
110+
// only 1 page of results left
111+
referrers = referrers[startIndex:]
112+
} else {
113+
referrers = referrers[startIndex : startIndex+pageSize]
114+
w.Header().Set("Link", generateLinkHeader(h.Repository.Named().Name(), h.Digest.String(), artifactTypeFilter, pageSize, pageNumber+1))
81115
}
82116

83117
response := v1.Index{
@@ -195,6 +229,19 @@ func readlink(ctx context.Context, path string, stDriver driver.StorageDriver) (
195229
return digest.Parse(string(content))
196230
}
197231

232+
func generateLinkHeader(repoName, subjectDigest, artifactType string, pageSize int, pageNumber int) string {
233+
linkURL := fmt.Sprintf("/v2/%s/referrers/%s", repoName, subjectDigest)
234+
v := url.Values{}
235+
v.Add("p", strconv.Itoa(pageNumber))
236+
if artifactType != "" {
237+
v.Add("artifactType", artifactType)
238+
}
239+
if pageSize > 0 {
240+
v.Add("n", strconv.Itoa(pageSize))
241+
}
242+
return fmt.Sprintf("<%s?%s>; rel=\"next\"", linkURL, v.Encode())
243+
}
244+
198245
func generateReferrerFromArtifact(ctx context.Context,
199246
blobStatter distribution.BlobStatter,
200247
referrerDigest digest.Digest,

0 commit comments

Comments
 (0)