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:
Andrei Aaron
2023-01-26 00:06:02 +02:00
committed by GitHub
parent be4b8c6243
commit feb7328f50
15 changed files with 1525 additions and 482 deletions
+223 -4
View File
@@ -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
},