freeform querry api

Signed-off-by: Laurentiu Niculae <themelopeus@gmail.com>
This commit is contained in:
Laurentiu Niculae
2022-07-12 15:58:04 +03:00
committed by Ramkumar Chinchani
parent a31869f270
commit 7e3d063319
20 changed files with 3597 additions and 562 deletions
+177 -1
View File
@@ -5,7 +5,9 @@ package search
// It serves as dependency injection for your app, add any dependencies you require here.
import (
"sort"
"strconv"
"strings"
godigest "github.com/opencontainers/go-digest"
"zotregistry.io/zot/pkg/log" // nolint: gci
@@ -123,7 +125,7 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*
r.log.Info().Msg("no repositories found")
}
layoutUtils := common.NewOciLayoutUtils(r.storeController, r.log)
layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log)
for _, repo := range repoList {
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
@@ -186,6 +188,180 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*
return results, nil
}
func cleanQuerry(query string) string {
query = strings.ToLower(query)
query = strings.Replace(query, ":", " ", 1)
return query
}
func globalSearch(repoList []string, name, tag string, olu common.OciLayoutUtils, log log.Logger) (
[]*gql_generated.RepoSummary, []*gql_generated.ImageSummary, []*gql_generated.LayerSummary,
) {
repos := []*gql_generated.RepoSummary{}
images := []*gql_generated.ImageSummary{}
layers := []*gql_generated.LayerSummary{}
for _, repo := range repoList {
repo := repo
// map used for dedube if 2 images reference the same blob
repoLayerBlob2Size := make(map[string]int64, 10)
// made up of all manifests, configs and image layers
repoSize := int64(0)
lastUpdate, err := olu.GetRepoLastUpdated(repo)
if err != nil {
log.Error().Err(err).Msgf("can't find latest update timestamp for repo: %s", repo)
}
tagsInfo, err := olu.GetImageTagsWithTimestamp(repo)
if err != nil {
log.Error().Err(err).Msgf("can't get tags info for repo: %s", repo)
continue
}
repoInfo, err := olu.GetExpandedRepoInfo(repo)
if err != nil {
log.Error().Err(err).Msgf("can't get repo info for repo: %s", repo)
continue
}
repoPlatforms := make([]*gql_generated.OsArch, 0, len(tagsInfo))
repoVendors := make([]*string, 0, len(repoInfo.Manifests))
for i, manifest := range repoInfo.Manifests {
imageLayersSize := int64(0)
manifestSize := olu.GetImageManifestSize(repo, godigest.Digest(tagsInfo[i].Digest))
configSize := olu.GetImageConfigSize(repo, godigest.Digest(tagsInfo[i].Digest))
for _, layer := range manifest.Layers {
layer := layer
layerSize, err := strconv.ParseInt(layer.Size, 10, 64)
if err != nil {
log.Error().Err(err).Msg("invalid layer size")
continue
}
repoLayerBlob2Size[layer.Digest] = layerSize
imageLayersSize += layerSize
// if we have a tag we won't match a layer
if tag != "" {
continue
}
if index := strings.Index(layer.Digest, name); index != -1 {
layers = append(layers, &gql_generated.LayerSummary{
Digest: &layer.Digest,
Size: &layer.Size,
Score: &index,
})
}
}
imageSize := imageLayersSize + manifestSize + configSize
repoSize += manifestSize + configSize
index := strings.Index(repo, name)
matchesTag := strings.HasPrefix(manifest.Tag, tag)
if index != -1 {
tag := manifest.Tag
size := strconv.Itoa(int(imageSize))
vendor := olu.GetImageVendor(repo, godigest.Digest(tagsInfo[i].Digest))
lastUpdated := olu.GetImageLastUpdated(repo, godigest.Digest(tagsInfo[i].Digest))
isSigned := manifest.IsSigned
// update matching score
score := calculateImageMatchingScore(repo, index, matchesTag)
os, arch := olu.GetImagePlatform(repo, godigest.Digest(tagsInfo[i].Digest))
osArch := &gql_generated.OsArch{
Os: &os,
Arch: &arch,
}
repoPlatforms = append(repoPlatforms, osArch)
repoVendors = append(repoVendors, &vendor)
images = append(images, &gql_generated.ImageSummary{
RepoName: &repo,
Tag: &tag,
LastUpdated: &lastUpdated,
IsSigned: &isSigned,
Size: &size,
Platform: osArch,
Vendor: &vendor,
Score: &score,
})
}
}
for layerBlob := range repoLayerBlob2Size {
repoSize += repoLayerBlob2Size[layerBlob]
}
if index := strings.Index(repo, name); index != -1 {
repoSize := strconv.FormatInt(repoSize, 10)
repos = append(repos, &gql_generated.RepoSummary{
Name: &repo,
LastUpdated: &lastUpdate,
Size: &repoSize,
Platforms: repoPlatforms,
Vendors: repoVendors,
Score: &index,
})
}
}
sort.Slice(repos, func(i, j int) bool {
return *repos[i].Score < *repos[j].Score
})
sort.Slice(images, func(i, j int) bool {
return *images[i].Score < *images[j].Score
})
sort.Slice(layers, func(i, j int) bool {
return *layers[i].Score < *layers[j].Score
})
return repos, images, layers
}
// calcalculateImageMatchingScore iterated from the index of the matched string in the
// artifact name until the beginning of the string or until delimitator "/".
// The distance represents the score of the match.
//
// Example:
// query: image
// repos: repo/test/myimage
// Score will be 2.
func calculateImageMatchingScore(artefactName string, index int, matchesTag bool) int {
score := 0
for index >= 1 {
if artefactName[index-1] == '/' {
break
}
index--
score++
}
if !matchesTag {
score += 10
}
return score
}
func getGraphqlCompatibleTags(fixedTags []common.TagInfo) []*gql_generated.TagInfo {
finalTagList := make([]*gql_generated.TagInfo, 0)