Skip to content

Commit

Permalink
feat(metadb): add support for querying for images by a blob digest (#…
Browse files Browse the repository at this point in the history
…2077)

Signed-off-by: Laurentiu Niculae <[email protected]>
  • Loading branch information
laurentiuNiculae authored Nov 27, 2023
1 parent 02a8ed7 commit 0de2210
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 78 deletions.
1 change: 1 addition & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ var (
ErrBadHTTPStatusCode = errors.New("cli: the response doesn't contain the expected status code")
ErrFormatNotSupported = errors.New("cli: the given output format is not supported")
ErrAPINotSupported = errors.New("registry at the given address doesn't implement the correct API")
ErrInvalidSearchQuery = errors.New("invalid search query")
ErrFileAlreadyCancelled = errors.New("storageDriver: file already cancelled")
ErrFileAlreadyClosed = errors.New("storageDriver: file already closed")
ErrFileAlreadyCommitted = errors.New("storageDriver: file already committed")
Expand Down
106 changes: 75 additions & 31 deletions pkg/extensions/search/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ func FilterByDigest(digest string) mTypes.FilterFunc {
// imageMeta will always contain 1 manifest
return func(repoMeta mTypes.RepoMeta, imageMeta mTypes.ImageMeta) bool {
lookupDigest := digest
contains := false

// Check in case of an index if the index digest matches the search digest
// For Manifests, this is equivalent to imageMeta.Manifests[0]
if imageMeta.Digest.String() == lookupDigest {
return true
}

manifest := imageMeta.Manifests[0]

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

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

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

return contains
return false
}
}

Expand Down Expand Up @@ -628,18 +633,10 @@ func globalSearch(ctx context.Context, query string, metaDB mTypes.MetaDB, filte
}
}

if searchingForRepos(query) {
skip := convert.SkipQGLField{
Vulnerabilities: canSkipField(preloads, "Repos.NewestImage.Vulnerabilities"),
}

pageInput := pagination.PageInput{
Limit: deref(requestedPage.Limit, 0),
Offset: deref(requestedPage.Offset, 0),
SortBy: pagination.SortCriteria(
deref(requestedPage.SortBy, gql_generated.SortCriteriaRelevance),
),
}
switch getSearchTarget(query) {
case RepoTarget:
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Repos.NewestImage.Vulnerabilities")}
pageInput := getPageInput(requestedPage)

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

paginatedRepos.Results = repos
} else { // search for images
skip := convert.SkipQGLField{
Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities"),
case ImageTarget:
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities")}
pageInput := getPageInput(requestedPage)

fullImageMetaList, err := metaDB.SearchTags(ctx, query)
if err != nil {
return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err
}

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

fullImageMetaList, err := metaDB.SearchTags(ctx, query)
images = imageSummaries

paginatedRepos.Page = &gql_generated.PageInfo{
TotalCount: pageInfo.TotalCount,
ItemCount: pageInfo.ItemCount,
}
case DigestTarget:
skip := convert.SkipQGLField{Vulnerabilities: canSkipField(preloads, "Images.Vulnerabilities")}
pageInput := getPageInput(requestedPage)

searchedDigest := query

fullImageMetaList, err := metaDB.FilterTags(ctx, mTypes.AcceptAllRepoTag, FilterByDigest(searchedDigest))
if err != nil {
return &gql_generated.PaginatedReposResult{}, []*gql_generated.ImageSummary{}, []*gql_generated.LayerSummary{}, err
}
Expand All @@ -697,11 +708,48 @@ func globalSearch(ctx context.Context, query string, metaDB mTypes.MetaDB, filte
TotalCount: pageInfo.TotalCount,
ItemCount: pageInfo.ItemCount,
}
default:
return &paginatedRepos, images, layers, zerr.ErrInvalidSearchQuery
}

return &paginatedRepos, images, layers, nil
}

func getPageInput(requestedPage *gql_generated.PageInput) pagination.PageInput {
return pagination.PageInput{
Limit: deref(requestedPage.Limit, 0),
Offset: deref(requestedPage.Offset, 0),
SortBy: pagination.SortCriteria(
deref(requestedPage.SortBy, gql_generated.SortCriteriaRelevance),
),
}
}

type SearchTarget int

const (
RepoTarget = iota
ImageTarget
DigestTarget
InvalidTarget
)

func getSearchTarget(query string) SearchTarget {
if !strings.ContainsAny(query, ":@") {
return RepoTarget
}

if strings.HasPrefix(query, string(godigest.SHA256)+":") {
return DigestTarget
}

if before, _, found := strings.Cut(query, ":"); found && before != "" {
return ImageTarget
}

return InvalidTarget
}

func canSkipField(preloads map[string]bool, s string) bool {
fieldIsPresent := preloads[s]

Expand Down Expand Up @@ -1100,10 +1148,6 @@ func deref[T any](pointer *T, defaultVal T) T {
return defaultVal
}

func searchingForRepos(query string) bool {
return !strings.Contains(query, ":")
}

func getImageList(ctx context.Context, repo string, metaDB mTypes.MetaDB, cveInfo cveinfo.CveInfo,
requestedPage *gql_generated.PageInput, log log.Logger, //nolint:unparam
) (*gql_generated.PaginatedImagesResult, error) {
Expand Down
Loading

0 comments on commit 0de2210

Please sign in to comment.