mirror of
https://github.com/project-zot/zot.git
synced 2024-12-23 22:27:35 -05:00
e8e7c343ad
* feat(repodb): add pagination for ImageListForDigest and implement FilterTags ImageListForDigest can now return paginated results, directly from DB. It uses FilterTags, a new method to filter tags (obviously) based on the criteria provided in the filter function. Pagination of tags is now slightly different, it shows all results if no limit and offset are provided. Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro> bug(tests): cli tests for digests expecting wrong size Signed-off-by: Andrei Aaron <aaaron@luxoft.com> (cherry picked from commit 369216df931a4053c18278a8d89f86d2e1e6a436) fix(repodb): do not include repo metadata in search results if no matching tags are identified Signed-off-by: Andrei Aaron <aaaron@luxoft.com> * fix(repodb): Fix an issue in FilterTags where repometa was not proceesed correctly The filter function was called only once per manifest digest. The function is supposed to also take into consideration repometa, but only the first repometa-manifestmeta pair was processed. Also increase code coverage. Signed-off-by: Andrei Aaron <aaaron@luxoft.com> Signed-off-by: Andrei Aaron <aaaron@luxoft.com> Co-authored-by: Alex Stan <alexandrustan96@yahoo.ro>
250 lines
5.3 KiB
Go
250 lines
5.3 KiB
Go
package repodb
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
)
|
|
|
|
// PageFinder permits keeping a pool of objects using Add
|
|
// and returning a specific page.
|
|
type PageFinder interface {
|
|
// Add
|
|
Add(detailedRepoMeta DetailedRepoMeta)
|
|
Page() []RepoMetadata
|
|
Reset()
|
|
}
|
|
|
|
// RepoPageFinder implements PageFinder. It manages RepoMeta objects and calculates the page
|
|
// using the given limit, offset and sortBy option.
|
|
type RepoPageFinder struct {
|
|
limit int
|
|
offset int
|
|
sortBy SortCriteria
|
|
pageBuffer []DetailedRepoMeta
|
|
}
|
|
|
|
func NewBaseRepoPageFinder(limit, offset int, sortBy SortCriteria) (*RepoPageFinder, error) {
|
|
if sortBy == "" {
|
|
sortBy = AlphabeticAsc
|
|
}
|
|
|
|
if limit < 0 {
|
|
return nil, zerr.ErrLimitIsNegative
|
|
}
|
|
|
|
if offset < 0 {
|
|
return nil, zerr.ErrOffsetIsNegative
|
|
}
|
|
|
|
if _, found := SortFunctions()[sortBy]; !found {
|
|
return nil, errors.Wrapf(zerr.ErrSortCriteriaNotSupported, "sorting repos by '%s' is not supported", sortBy)
|
|
}
|
|
|
|
return &RepoPageFinder{
|
|
limit: limit,
|
|
offset: offset,
|
|
sortBy: sortBy,
|
|
pageBuffer: make([]DetailedRepoMeta, 0, limit),
|
|
}, nil
|
|
}
|
|
|
|
func (bpt *RepoPageFinder) Reset() {
|
|
bpt.pageBuffer = []DetailedRepoMeta{}
|
|
}
|
|
|
|
func (bpt *RepoPageFinder) Add(namedRepoMeta DetailedRepoMeta) {
|
|
bpt.pageBuffer = append(bpt.pageBuffer, namedRepoMeta)
|
|
}
|
|
|
|
func (bpt *RepoPageFinder) Page() []RepoMetadata {
|
|
if len(bpt.pageBuffer) == 0 {
|
|
return []RepoMetadata{}
|
|
}
|
|
|
|
sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer))
|
|
|
|
// the offset and limit are calculatd in terms of repos counted
|
|
start := bpt.offset
|
|
end := bpt.offset + bpt.limit
|
|
|
|
// we'll return an empty array when the offset is greater than the number of elements
|
|
if start >= len(bpt.pageBuffer) {
|
|
start = len(bpt.pageBuffer)
|
|
end = start
|
|
}
|
|
|
|
if end >= len(bpt.pageBuffer) {
|
|
end = len(bpt.pageBuffer)
|
|
}
|
|
|
|
detailedReposPage := bpt.pageBuffer[start:end]
|
|
|
|
if start == 0 && end == 0 {
|
|
detailedReposPage = bpt.pageBuffer
|
|
}
|
|
|
|
repos := make([]RepoMetadata, 0, len(detailedReposPage))
|
|
|
|
for _, drm := range detailedReposPage {
|
|
repos = append(repos, drm.RepoMeta)
|
|
}
|
|
|
|
return repos
|
|
}
|
|
|
|
type ImagePageFinder struct {
|
|
limit int
|
|
offset int
|
|
sortBy SortCriteria
|
|
pageBuffer []DetailedRepoMeta
|
|
}
|
|
|
|
func NewBaseImagePageFinder(limit, offset int, sortBy SortCriteria) (*ImagePageFinder, error) {
|
|
if sortBy == "" {
|
|
sortBy = AlphabeticAsc
|
|
}
|
|
|
|
if limit < 0 {
|
|
return nil, zerr.ErrLimitIsNegative
|
|
}
|
|
|
|
if offset < 0 {
|
|
return nil, zerr.ErrOffsetIsNegative
|
|
}
|
|
|
|
if _, found := SortFunctions()[sortBy]; !found {
|
|
return nil, errors.Wrapf(zerr.ErrSortCriteriaNotSupported, "sorting repos by '%s' is not supported", sortBy)
|
|
}
|
|
|
|
return &ImagePageFinder{
|
|
limit: limit,
|
|
offset: offset,
|
|
sortBy: sortBy,
|
|
pageBuffer: make([]DetailedRepoMeta, 0, limit),
|
|
}, nil
|
|
}
|
|
|
|
func (bpt *ImagePageFinder) Reset() {
|
|
bpt.pageBuffer = []DetailedRepoMeta{}
|
|
}
|
|
|
|
func (bpt *ImagePageFinder) Add(namedRepoMeta DetailedRepoMeta) {
|
|
bpt.pageBuffer = append(bpt.pageBuffer, namedRepoMeta)
|
|
}
|
|
|
|
func (bpt *ImagePageFinder) Page() []RepoMetadata {
|
|
if len(bpt.pageBuffer) == 0 {
|
|
return []RepoMetadata{}
|
|
}
|
|
|
|
sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer))
|
|
|
|
repoStartIndex := 0
|
|
tagStartIndex := 0
|
|
|
|
// the offset and limit are calculatd in terms of tags counted
|
|
remainingOffset := bpt.offset
|
|
remainingLimit := bpt.limit
|
|
|
|
repos := make([]RepoMetadata, 0)
|
|
|
|
if remainingOffset == 0 && remainingLimit == 0 {
|
|
for _, drm := range bpt.pageBuffer {
|
|
repo := drm.RepoMeta
|
|
repos = append(repos, repo)
|
|
}
|
|
|
|
return repos
|
|
}
|
|
|
|
// bring cursor to position in RepoMeta array
|
|
for _, drm := range bpt.pageBuffer {
|
|
if remainingOffset < len(drm.RepoMeta.Tags) {
|
|
tagStartIndex = remainingOffset
|
|
|
|
break
|
|
}
|
|
|
|
remainingOffset -= len(drm.RepoMeta.Tags)
|
|
repoStartIndex++
|
|
}
|
|
|
|
// offset is larger than the number of tags
|
|
if repoStartIndex >= len(bpt.pageBuffer) {
|
|
return []RepoMetadata{}
|
|
}
|
|
|
|
// finish counting remaining tags inside the first repo meta
|
|
partialTags := map[string]Descriptor{}
|
|
firstRepoMeta := bpt.pageBuffer[repoStartIndex].RepoMeta
|
|
|
|
tags := make([]string, 0, len(firstRepoMeta.Tags))
|
|
for k := range firstRepoMeta.Tags {
|
|
tags = append(tags, k)
|
|
}
|
|
|
|
sort.Strings(tags)
|
|
|
|
for i := tagStartIndex; i < len(tags); i++ {
|
|
tag := tags[i]
|
|
|
|
partialTags[tag] = firstRepoMeta.Tags[tag]
|
|
remainingLimit--
|
|
|
|
if remainingLimit == 0 {
|
|
firstRepoMeta.Tags = partialTags
|
|
repos = append(repos, firstRepoMeta)
|
|
|
|
return repos
|
|
}
|
|
}
|
|
|
|
firstRepoMeta.Tags = partialTags
|
|
repos = append(repos, firstRepoMeta)
|
|
repoStartIndex++
|
|
|
|
// continue with the remaining repos
|
|
for i := repoStartIndex; i < len(bpt.pageBuffer); i++ {
|
|
repoMeta := bpt.pageBuffer[i].RepoMeta
|
|
|
|
if len(repoMeta.Tags) > remainingLimit {
|
|
partialTags := map[string]Descriptor{}
|
|
|
|
tags := make([]string, 0, len(repoMeta.Tags))
|
|
for k := range repoMeta.Tags {
|
|
tags = append(tags, k)
|
|
}
|
|
|
|
sort.Strings(tags)
|
|
|
|
for _, tag := range tags {
|
|
partialTags[tag] = repoMeta.Tags[tag]
|
|
remainingLimit--
|
|
|
|
if remainingLimit == 0 {
|
|
repoMeta.Tags = partialTags
|
|
repos = append(repos, repoMeta)
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
return repos
|
|
}
|
|
|
|
// add the whole repo
|
|
repos = append(repos, repoMeta)
|
|
remainingLimit -= len(repoMeta.Tags)
|
|
|
|
if remainingLimit == 0 {
|
|
return repos
|
|
}
|
|
}
|
|
|
|
// we arrive here when the limit is bigger than the number of tags
|
|
|
|
return repos
|
|
}
|