Skip to content

Commit 0de2210

Browse files
feat(metadb): add support for querying for images by a blob digest (#2077)
Signed-off-by: Laurentiu Niculae <[email protected]>
1 parent 02a8ed7 commit 0de2210

File tree

10 files changed

+271
-78
lines changed

10 files changed

+271
-78
lines changed

errors/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ var (
160160
ErrBadHTTPStatusCode = errors.New("cli: the response doesn't contain the expected status code")
161161
ErrFormatNotSupported = errors.New("cli: the given output format is not supported")
162162
ErrAPINotSupported = errors.New("registry at the given address doesn't implement the correct API")
163+
ErrInvalidSearchQuery = errors.New("invalid search query")
163164
ErrFileAlreadyCancelled = errors.New("storageDriver: file already cancelled")
164165
ErrFileAlreadyClosed = errors.New("storageDriver: file already closed")
165166
ErrFileAlreadyCommitted = errors.New("storageDriver: file already committed")

pkg/extensions/search/resolver.go

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,12 @@ func FilterByDigest(digest string) mTypes.FilterFunc {
7676
// imageMeta will always contain 1 manifest
7777
return func(repoMeta mTypes.RepoMeta, imageMeta mTypes.ImageMeta) bool {
7878
lookupDigest := digest
79-
contains := false
79+
80+
// Check in case of an index if the index digest matches the search digest
81+
// For Manifests, this is equivalent to imageMeta.Manifests[0]
82+
if imageMeta.Digest.String() == lookupDigest {
83+
return true
84+
}
8085

8186
manifest := imageMeta.Manifests[0]
8287

@@ -85,24 +90,24 @@ func FilterByDigest(digest string) mTypes.FilterFunc {
8590
// Check the image manifest in index.json matches the search digest
8691
// This is a blob with mediaType application/vnd.oci.image.manifest.v1+json
8792
if strings.Contains(manifestDigest, lookupDigest) {
88-
contains = true
93+
return true
8994
}
9095

9196
// Check the image config matches the search digest
9297
// This is a blob with mediaType application/vnd.oci.image.config.v1+json
9398
if strings.Contains(manifest.Manifest.Config.Digest.String(), lookupDigest) {
94-
contains = true
99+
return true
95100
}
96101

97102
// Check to see if the individual layers in the oci image manifest match the digest
98103
// These are blobs with mediaType application/vnd.oci.image.layer.v1.tar+gzip
99104
for _, layer := range manifest.Manifest.Layers {
100105
if strings.Contains(layer.Digest.String(), lookupDigest) {
101-
contains = true
106+
return true
102107
}
103108
}
104109

105-
return contains
110+
return false
106111
}
107112
}
108113

@@ -628,18 +633,10 @@ func globalSearch(ctx context.Context, query string, metaDB mTypes.MetaDB, filte
628633
}
629634
}
630635

631-
if searchingForRepos(query) {
632-
skip := convert.SkipQGLField{
633-
Vulnerabilities: canSkipField(preloads, "Repos.NewestImage.Vulnerabilities"),
634-
}
635-
636-
pageInput := pagination.PageInput{
637-
Limit: deref(requestedPage.Limit, 0),
638-
Offset: deref(requestedPage.Offset, 0),
639-
SortBy: pagination.SortCriteria(
640-
deref(requestedPage.SortBy, gql_generated.SortCriteriaRelevance),
641-
),
642-
}
636+
switch getSearchTarget(query) {
637+
case RepoTarget:
638+
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Repos.NewestImage.Vulnerabilities")}
639+
pageInput := getPageInput(requestedPage)
643640

644641
repoMetaList, err := metaDB.SearchRepos(ctx, query)
645642
if err != nil {
@@ -667,20 +664,34 @@ func globalSearch(ctx context.Context, query string, metaDB mTypes.MetaDB, filte
667664
}
668665

669666
paginatedRepos.Results = repos
670-
} else { // search for images
671-
skip := convert.SkipQGLField{
672-
Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities"),
667+
case ImageTarget:
668+
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities")}
669+
pageInput := getPageInput(requestedPage)
670+
671+
fullImageMetaList, err := metaDB.SearchTags(ctx, query)
672+
if err != nil {
673+
return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err
673674
}
674675

675-
pageInput := pagination.PageInput{
676-
Limit: deref(requestedPage.Limit, 0),
677-
Offset: deref(requestedPage.Offset, 0),
678-
SortBy: pagination.SortCriteria(
679-
deref(requestedPage.SortBy, gql_generated.SortCriteriaRelevance),
680-
),
676+
imageSummaries, pageInfo, err := convert.PaginatedFullImageMeta2ImageSummaries(ctx, fullImageMetaList, skip, cveInfo,
677+
localFilter, pageInput)
678+
if err != nil {
679+
return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err
681680
}
682681

683-
fullImageMetaList, err := metaDB.SearchTags(ctx, query)
682+
images = imageSummaries
683+
684+
paginatedRepos.Page = &gql_generated.PageInfo{
685+
TotalCount: pageInfo.TotalCount,
686+
ItemCount: pageInfo.ItemCount,
687+
}
688+
case DigestTarget:
689+
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities")}
690+
pageInput := getPageInput(requestedPage)
691+
692+
searchedDigest := query
693+
694+
fullImageMetaList, err := metaDB.FilterTags(ctx, mTypes.AcceptAllRepoTag, FilterByDigest(searchedDigest))
684695
if err != nil {
685696
return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err
686697
}
@@ -697,11 +708,48 @@ func globalSearch(ctx context.Context, query string, metaDB mTypes.MetaDB, filte
697708
TotalCount: pageInfo.TotalCount,
698709
ItemCount: pageInfo.ItemCount,
699710
}
711+
default:
712+
return &paginatedRepos, images, layers, zerr.ErrInvalidSearchQuery
700713
}
701714

702715
return &paginatedRepos, images, layers, nil
703716
}
704717

718+
func getPageInput(requestedPage *gql_generated.PageInput) pagination.PageInput {
719+
return pagination.PageInput{
720+
Limit: deref(requestedPage.Limit, 0),
721+
Offset: deref(requestedPage.Offset, 0),
722+
SortBy: pagination.SortCriteria(
723+
deref(requestedPage.SortBy, gql_generated.SortCriteriaRelevance),
724+
),
725+
}
726+
}
727+
728+
type SearchTarget int
729+
730+
const (
731+
RepoTarget = iota
732+
ImageTarget
733+
DigestTarget
734+
InvalidTarget
735+
)
736+
737+
func getSearchTarget(query string) SearchTarget {
738+
if !strings.ContainsAny(query, ":@") {
739+
return RepoTarget
740+
}
741+
742+
if strings.HasPrefix(query, string(godigest.SHA256)+":") {
743+
return DigestTarget
744+
}
745+
746+
if before, _, found := strings.Cut(query, ":"); found && before != "" {
747+
return ImageTarget
748+
}
749+
750+
return InvalidTarget
751+
}
752+
705753
func canSkipField(preloads map[string]bool, s string) bool {
706754
fieldIsPresent := preloads[s]
707755

@@ -1100,10 +1148,6 @@ func deref[T any](pointer *T, defaultVal T) T {
11001148
return defaultVal
11011149
}
11021150

1103-
func searchingForRepos(query string) bool {
1104-
return !strings.Contains(query, ":")
1105-
}
1106-
11071151
func getImageList(ctx context.Context, repo string, metaDB mTypes.MetaDB, cveInfo cveinfo.CveInfo,
11081152
requestedPage *gql_generated.PageInput, log log.Logger, //nolint:unparam
11091153
) (*gql_generated.PaginatedImagesResult, error) {

0 commit comments

Comments
 (0)