commit
						d80a63f1ea
					
				|  | @ -1142,6 +1142,7 @@ The error codes encountered via the API are enumerated in the following table: | ||||||
|  `MANIFEST_UNVERIFIED` | manifest failed signature verification | During manifest upload, if the manifest fails signature verification, this error will be returned. |  `MANIFEST_UNVERIFIED` | manifest failed signature verification | During manifest upload, if the manifest fails signature verification, this error will be returned. | ||||||
|  `NAME_INVALID` | invalid repository name | Invalid repository name encountered either during manifest validation or any API operation. |  `NAME_INVALID` | invalid repository name | Invalid repository name encountered either during manifest validation or any API operation. | ||||||
|  `NAME_UNKNOWN` | repository name not known to registry | This is returned if the name used during an operation is unknown to the registry. |  `NAME_UNKNOWN` | repository name not known to registry | This is returned if the name used during an operation is unknown to the registry. | ||||||
|  |  `PAGINATION_NUMBER_INVALID` | invalid number of results requested | Returned when the `n` parameter (number of results to return) is not an integer, or `n` is negative. | ||||||
|  `SIZE_INVALID` | provided length did not match content length | When a layer is uploaded, the provided size will be checked against the uploaded content. If they do not match, this error will be returned. |  `SIZE_INVALID` | provided length did not match content length | When a layer is uploaded, the provided size will be checked against the uploaded content. If they do not match, this error will be returned. | ||||||
|  `TAG_INVALID` | manifest tag did not match URI | During a manifest upload, if the tag in the manifest does not match the uri tag, this error will be returned. |  `TAG_INVALID` | manifest tag did not match URI | During a manifest upload, if the tag in the manifest does not match the uri tag, this error will be returned. | ||||||
|  `UNAUTHORIZED` | authentication required | The access controller was unable to authenticate the client. Often this will be accompanied by a Www-Authenticate HTTP response header indicating how to authenticate. |  `UNAUTHORIZED` | authentication required | The access controller was unable to authenticate the client. Often this will be accompanied by a Www-Authenticate HTTP response header indicating how to authenticate. | ||||||
|  |  | ||||||
|  | @ -490,6 +490,18 @@ var routeDescriptors = []RouteDescriptor{ | ||||||
| 							}, | 							}, | ||||||
| 						}, | 						}, | ||||||
| 						Failures: []ResponseDescriptor{ | 						Failures: []ResponseDescriptor{ | ||||||
|  | 							{ | ||||||
|  | 								Name:        "Invalid pagination number", | ||||||
|  | 								Description: "The received parameter n was invalid in some way, as described by the error code. The client should resolve the issue and retry the request.", | ||||||
|  | 								StatusCode:  http.StatusBadRequest, | ||||||
|  | 								Body: BodyDescriptor{ | ||||||
|  | 									ContentType: "application/json", | ||||||
|  | 									Format:      errorsBody, | ||||||
|  | 								}, | ||||||
|  | 								ErrorCodes: []errcode.ErrorCode{ | ||||||
|  | 									ErrorCodePaginationNumberInvalid, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
| 							unauthorizedResponseDescriptor, | 							unauthorizedResponseDescriptor, | ||||||
| 							repositoryNotFoundResponseDescriptor, | 							repositoryNotFoundResponseDescriptor, | ||||||
| 							deniedResponseDescriptor, | 							deniedResponseDescriptor, | ||||||
|  |  | ||||||
|  | @ -144,4 +144,14 @@ var ( | ||||||
| 		longer proceed.`, | 		longer proceed.`, | ||||||
| 		HTTPStatusCode: http.StatusNotFound, | 		HTTPStatusCode: http.StatusNotFound, | ||||||
| 	}) | 	}) | ||||||
|  | 
 | ||||||
|  | 	// ErrorCodePaginationNumberInvalid is returned when the `n` parameter is
 | ||||||
|  | 	// not an integer, or `n` is negative.
 | ||||||
|  | 	ErrorCodePaginationNumberInvalid = errcode.Register(errGroup, errcode.ErrorDescriptor{ | ||||||
|  | 		Value:   "PAGINATION_NUMBER_INVALID", | ||||||
|  | 		Message: "invalid number of results requested", | ||||||
|  | 		Description: `Returned when the "n" parameter (number of results | ||||||
|  | 		to return) is not an integer, or "n" is negative.`, | ||||||
|  | 		HTTPStatusCode: http.StatusBadRequest, | ||||||
|  | 	}) | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ package handlers | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"sort" | ||||||
|  | 	"strconv" | ||||||
| 
 | 
 | ||||||
| 	"github.com/distribution/distribution/v3" | 	"github.com/distribution/distribution/v3" | ||||||
| 	"github.com/distribution/distribution/v3/registry/api/errcode" | 	"github.com/distribution/distribution/v3/registry/api/errcode" | ||||||
|  | @ -49,6 +51,51 @@ func (th *tagsHandler) GetTags(w http.ResponseWriter, r *http.Request) { | ||||||
| 		return | 		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") | 	w.Header().Set("Content-Type", "application/json") | ||||||
| 
 | 
 | ||||||
| 	enc := json.NewEncoder(w) | 	enc := json.NewEncoder(w) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ package storage | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"sort" | ||||||
| 
 | 
 | ||||||
| 	"github.com/distribution/distribution/v3" | 	"github.com/distribution/distribution/v3" | ||||||
| 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" | 	storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" | ||||||
|  | @ -47,6 +48,10 @@ func (ts *tagStore) All(ctx context.Context) ([]string, error) { | ||||||
| 		tags = append(tags, filename) | 		tags = append(tags, filename) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// there is no guarantee for the order,
 | ||||||
|  | 	// therefore sort before return.
 | ||||||
|  | 	sort.Strings(tags) | ||||||
|  | 
 | ||||||
| 	return tags, nil | 	return tags, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue