mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
feat(repodb): DerivedImageList and BaseImageList make use of RepoDB (#1135)
- derivedImageList and baseImageList now use FilterTags to obtain results, each with its own filter function - images that have the exact same manifest as the one provided as a parameter are no longer considered base images or derived images - both calls can be made with specific pagination parameters, and the response will include PageInfo Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro> fix(tests): fix one of the pagination tests The results were not reliable as the 2 returned tags were sorted by created date/time which was not set, resulting in an unpredictable order Signed-off-by: Andrei Aaron <andaaron@cisco.com> (cherry picked from commit be504200a1127371422aeb0e5c0219e2a1ead20a) (cherry picked from commit ed8d797e639f262a63840120afe92da7db9a7600) Signed-off-by: Andrei Aaron <aaaron@luxoft.com> Signed-off-by: Andrei Aaron <andaaron@cisco.com> Signed-off-by: Andrei Aaron <aaaron@luxoft.com> Co-authored-by: Alex Stan <alexandrustan96@yahoo.ro>
This commit is contained in:
@@ -137,7 +137,7 @@ func getImageListForDigest(ctx context.Context, digest string, repoDB repodb.Rep
|
||||
}
|
||||
|
||||
// get all repos
|
||||
reposMeta, manifestMetaMap, err := repoDB.FilterTags(ctx, FilterByDigest(digest), pageInput)
|
||||
reposMeta, manifestMetaMap, _, err := repoDB.FilterTags(ctx, FilterByDigest(digest), pageInput)
|
||||
if err != nil {
|
||||
return []*gql_generated.ImageSummary{}, err
|
||||
}
|
||||
@@ -336,7 +336,7 @@ func getImageListForCVE(
|
||||
}
|
||||
|
||||
// get all repos
|
||||
reposMeta, manifestMetaMap, err := repoDB.FilterTags(ctx, FilterByTagInfo(affectedImages), pageInput)
|
||||
reposMeta, manifestMetaMap, _, err := repoDB.FilterTags(ctx, FilterByTagInfo(affectedImages), pageInput)
|
||||
if err != nil {
|
||||
return []*gql_generated.ImageSummary{}, err
|
||||
}
|
||||
@@ -388,7 +388,7 @@ func getImageListWithCVEFixed(
|
||||
}
|
||||
|
||||
// get all repos
|
||||
reposMeta, manifestMetaMap, err := repoDB.FilterTags(ctx, FilterByTagInfo(tagsInfo), pageInput)
|
||||
reposMeta, manifestMetaMap, _, err := repoDB.FilterTags(ctx, FilterByTagInfo(tagsInfo), pageInput)
|
||||
if err != nil {
|
||||
return []*gql_generated.ImageSummary{}, err
|
||||
}
|
||||
@@ -542,6 +542,225 @@ func canSkipField(preloads map[string]bool, s string) bool {
|
||||
return !fieldIsPresent
|
||||
}
|
||||
|
||||
func derivedImageList(ctx context.Context, image string, repoDB repodb.RepoDB,
|
||||
requestedPage *gql_generated.PageInput,
|
||||
cveInfo cveinfo.CveInfo, log log.Logger,
|
||||
) (*gql_generated.PaginatedImagesResult, error) {
|
||||
derivedList := make([]*gql_generated.ImageSummary, 0)
|
||||
|
||||
if requestedPage == nil {
|
||||
requestedPage = &gql_generated.PageInput{}
|
||||
}
|
||||
|
||||
pageInput := repodb.PageInput{
|
||||
Limit: safeDerefferencing(requestedPage.Limit, 0),
|
||||
Offset: safeDerefferencing(requestedPage.Offset, 0),
|
||||
SortBy: repodb.SortCriteria(
|
||||
safeDerefferencing(requestedPage.SortBy, gql_generated.SortCriteriaUpdateTime),
|
||||
),
|
||||
}
|
||||
|
||||
skip := convert.SkipQGLField{
|
||||
Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Vulnerabilities"),
|
||||
}
|
||||
|
||||
imageRepo, imageTag := common.GetImageDirAndTag(image)
|
||||
if imageTag == "" {
|
||||
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided")
|
||||
}
|
||||
|
||||
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, repoDB, cveInfo, log)
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrRepoMetaNotFound) {
|
||||
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found")
|
||||
}
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{}, err
|
||||
}
|
||||
|
||||
// we need all available tags
|
||||
reposMeta, manifestMetaMap, pageInfo, err := repoDB.FilterTags(ctx,
|
||||
filterDerivedImages(searchedImage),
|
||||
pageInput)
|
||||
if err != nil {
|
||||
return &gql_generated.PaginatedImagesResult{}, err
|
||||
}
|
||||
|
||||
for _, repoMeta := range reposMeta {
|
||||
summary := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, skip, cveInfo)
|
||||
derivedList = append(derivedList, summary...)
|
||||
}
|
||||
|
||||
if len(derivedList) == 0 {
|
||||
log.Info().Msg("no images found")
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{
|
||||
Page: &gql_generated.PageInfo{},
|
||||
Results: derivedList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{
|
||||
Results: derivedList,
|
||||
Page: &gql_generated.PageInfo{
|
||||
TotalCount: pageInfo.TotalCount,
|
||||
ItemCount: pageInfo.ItemCount,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func filterDerivedImages(image *gql_generated.ImageSummary) repodb.FilterFunc {
|
||||
return func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool {
|
||||
var addImageToList bool
|
||||
|
||||
var imageManifest ispec.Manifest
|
||||
|
||||
err := json.Unmarshal(manifestMeta.ManifestBlob, &imageManifest)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
manifestDigest := godigest.FromBytes(manifestMeta.ManifestBlob).String()
|
||||
if manifestDigest == *image.Digest {
|
||||
return false
|
||||
}
|
||||
|
||||
imageLayers := image.Layers
|
||||
|
||||
addImageToList = false
|
||||
layers := imageManifest.Layers
|
||||
|
||||
sameLayer := 0
|
||||
|
||||
for _, l := range imageLayers {
|
||||
for _, k := range layers {
|
||||
if k.Digest.String() == *l.Digest {
|
||||
sameLayer++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if all layers are the same
|
||||
if sameLayer == len(imageLayers) {
|
||||
// it's a derived image
|
||||
addImageToList = true
|
||||
}
|
||||
|
||||
return addImageToList
|
||||
}
|
||||
}
|
||||
|
||||
func baseImageList(ctx context.Context, image string, repoDB repodb.RepoDB,
|
||||
requestedPage *gql_generated.PageInput,
|
||||
cveInfo cveinfo.CveInfo, log log.Logger,
|
||||
) (*gql_generated.PaginatedImagesResult, error) {
|
||||
imageSummaries := make([]*gql_generated.ImageSummary, 0)
|
||||
|
||||
if requestedPage == nil {
|
||||
requestedPage = &gql_generated.PageInput{}
|
||||
}
|
||||
|
||||
pageInput := repodb.PageInput{
|
||||
Limit: safeDerefferencing(requestedPage.Limit, 0),
|
||||
Offset: safeDerefferencing(requestedPage.Offset, 0),
|
||||
SortBy: repodb.SortCriteria(
|
||||
safeDerefferencing(requestedPage.SortBy, gql_generated.SortCriteriaUpdateTime),
|
||||
),
|
||||
}
|
||||
|
||||
skip := convert.SkipQGLField{
|
||||
Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Vulnerabilities"),
|
||||
}
|
||||
|
||||
imageRepo, imageTag := common.GetImageDirAndTag(image)
|
||||
|
||||
if imageTag == "" {
|
||||
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided")
|
||||
}
|
||||
|
||||
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, repoDB, cveInfo, log)
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrRepoMetaNotFound) {
|
||||
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found")
|
||||
}
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{}, err
|
||||
}
|
||||
|
||||
// we need all available tags
|
||||
reposMeta, manifestMetaMap, pageInfo, err := repoDB.FilterTags(ctx,
|
||||
filterBaseImages(searchedImage),
|
||||
pageInput)
|
||||
if err != nil {
|
||||
return &gql_generated.PaginatedImagesResult{}, err
|
||||
}
|
||||
|
||||
for _, repoMeta := range reposMeta {
|
||||
summary := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, skip, cveInfo)
|
||||
imageSummaries = append(imageSummaries, summary...)
|
||||
}
|
||||
|
||||
if len(imageSummaries) == 0 {
|
||||
log.Info().Msg("no images found")
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{
|
||||
Results: imageSummaries,
|
||||
Page: &gql_generated.PageInfo{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &gql_generated.PaginatedImagesResult{
|
||||
Page: &gql_generated.PageInfo{
|
||||
TotalCount: pageInfo.TotalCount,
|
||||
ItemCount: pageInfo.ItemCount,
|
||||
},
|
||||
Results: imageSummaries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func filterBaseImages(image *gql_generated.ImageSummary) repodb.FilterFunc {
|
||||
return func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool {
|
||||
var addImageToList bool
|
||||
|
||||
var imageManifest ispec.Manifest
|
||||
|
||||
err := json.Unmarshal(manifestMeta.ManifestBlob, &imageManifest)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
manifestDigest := godigest.FromBytes(manifestMeta.ManifestBlob).String()
|
||||
if manifestDigest == *image.Digest {
|
||||
return false
|
||||
}
|
||||
|
||||
imageLayers := image.Layers
|
||||
|
||||
addImageToList = true
|
||||
layers := imageManifest.Layers
|
||||
|
||||
for _, l := range layers {
|
||||
foundLayer := false
|
||||
|
||||
for _, k := range imageLayers {
|
||||
if l.Digest.String() == *k.Digest {
|
||||
foundLayer = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundLayer {
|
||||
addImageToList = false
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return addImageToList
|
||||
}
|
||||
}
|
||||
|
||||
func validateGlobalSearchInput(query string, filter *gql_generated.Filter,
|
||||
requestedPage *gql_generated.PageInput,
|
||||
) error {
|
||||
@@ -765,7 +984,7 @@ func getImageList(ctx context.Context, repo string, repoDB repodb.RepoDB, cveInf
|
||||
}
|
||||
|
||||
// reposMeta, manifestMetaMap, err := repoDB.SearchRepos(ctx, repo, repodb.Filter{}, pageInput)
|
||||
reposMeta, manifestMetaMap, err := repoDB.FilterTags(ctx,
|
||||
reposMeta, manifestMetaMap, _, err := repoDB.FilterTags(ctx,
|
||||
func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool {
|
||||
return true
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user