4da2712b52
Signed-off-by: eyjhb <eyjhbb@gmail.com>
109 lines
2.9 KiB
Go
109 lines
2.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/distribution/distribution/v3"
|
|
"github.com/distribution/distribution/v3/registry/api/errcode"
|
|
v2 "github.com/distribution/distribution/v3/registry/api/v2"
|
|
"github.com/gorilla/handlers"
|
|
)
|
|
|
|
// tagsDispatcher constructs the tags handler api endpoint.
|
|
func tagsDispatcher(ctx *Context, r *http.Request) http.Handler {
|
|
tagsHandler := &tagsHandler{
|
|
Context: ctx,
|
|
}
|
|
|
|
return handlers.MethodHandler{
|
|
"GET": http.HandlerFunc(tagsHandler.GetTags),
|
|
}
|
|
}
|
|
|
|
// tagsHandler handles requests for lists of tags under a repository name.
|
|
type tagsHandler struct {
|
|
*Context
|
|
}
|
|
|
|
type tagsAPIResponse struct {
|
|
Name string `json:"name"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
// GetTags returns a json list of tags for a specific image name.
|
|
func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) {
|
|
defer r.Body.Close()
|
|
|
|
tagService := th.Repository.Tags(th)
|
|
tags, err := tagService.All(th)
|
|
if err != nil {
|
|
switch err := err.(type) {
|
|
case distribution.ErrRepositoryUnknown:
|
|
th.Errors = append(th.Errors, v2.ErrorCodeNameUnknown.WithDetail(map[string]string{"name": th.Repository.Named().Name()}))
|
|
case errcode.Error:
|
|
th.Errors = append(th.Errors, err)
|
|
default:
|
|
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
|
}
|
|
return
|
|
}
|
|
|
|
// do pagination if requested
|
|
q := r.URL.Query()
|
|
// get entries after latest, if any specified
|
|
if lastEntry := q.Get("last"); lastEntry != "" {
|
|
lastEntryIndex := sort.SearchStrings(tags, lastEntry)
|
|
|
|
// as`sort.SearchStrings` can return len(tags), if the
|
|
// specified `lastEntry` is not found, we need to
|
|
// ensure it does not panic when slicing.
|
|
if lastEntryIndex == len(tags) {
|
|
tags = []string{}
|
|
} else {
|
|
tags = tags[lastEntryIndex+1:]
|
|
}
|
|
}
|
|
|
|
// if no error, means that the user requested `n` entries
|
|
if n := q.Get("n"); n != "" {
|
|
maxEntries, err := strconv.Atoi(n)
|
|
if err != nil || maxEntries < 0 {
|
|
th.Errors = append(th.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]string{"n": n}))
|
|
return
|
|
}
|
|
|
|
// if there is requested more than or
|
|
// equal to the amount of tags we have,
|
|
// then set the request to equal `len(tags)`.
|
|
// the reason for the `=`, is so the else
|
|
// clause will only activate if there
|
|
// are tags left the user needs.
|
|
if maxEntries >= len(tags) {
|
|
maxEntries = len(tags)
|
|
} else if maxEntries > 0 {
|
|
// defined in `catalog.go`
|
|
urlStr, err := createLinkEntry(r.URL.String(), maxEntries, tags[maxEntries-1])
|
|
if err != nil {
|
|
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
|
return
|
|
}
|
|
w.Header().Set("Link", urlStr)
|
|
}
|
|
|
|
tags = tags[:maxEntries]
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
enc := json.NewEncoder(w)
|
|
if err := enc.Encode(tagsAPIResponse{
|
|
Name: th.Repository.Named().Name(),
|
|
Tags: tags,
|
|
}); err != nil {
|
|
th.Errors = append(th.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
|
|
return
|
|
}
|
|
}
|