mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
e0d808b196
Return this data as part of GlobalSearch and RepoListWithNewestImage query results. This commit also includes refactoring of the CVE scanning logic in order to better encapsulate trivy specific logic, remove CVE scanning logic from the graphql resolver. Signed-off-by: Andrei Aaron <andaaron@cisco.com>
721 lines
20 KiB
Go
721 lines
20 KiB
Go
package search
|
|
|
|
// This file will not be regenerated automatically.
|
|
//
|
|
// It serves as dependency injection for your app, add any dependencies you require here.
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/99designs/gqlgen/graphql"
|
|
glob "github.com/bmatcuk/doublestar/v4" // nolint:gci
|
|
v1 "github.com/google/go-containerregistry/pkg/v1" // nolint:gci
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
|
"zotregistry.io/zot/pkg/extensions/search/common"
|
|
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
|
|
digestinfo "zotregistry.io/zot/pkg/extensions/search/digest"
|
|
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
|
|
"zotregistry.io/zot/pkg/log" // nolint: gci
|
|
localCtx "zotregistry.io/zot/pkg/requestcontext"
|
|
"zotregistry.io/zot/pkg/storage"
|
|
) // THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.
|
|
|
|
// Resolver ...
|
|
type Resolver struct {
|
|
cveInfo cveinfo.CveInfo
|
|
storeController storage.StoreController
|
|
digestInfo *digestinfo.DigestInfo
|
|
log log.Logger
|
|
}
|
|
|
|
var (
|
|
ErrBadCtxFormat = errors.New("type assertion failed")
|
|
ErrBadLayerCount = errors.New("manifest: layers count doesn't correspond to config history")
|
|
)
|
|
|
|
// GetResolverConfig ...
|
|
func GetResolverConfig(log log.Logger, storeController storage.StoreController, cveInfo cveinfo.CveInfo,
|
|
) gql_generated.Config {
|
|
digestInfo := digestinfo.NewDigestInfo(storeController, log)
|
|
|
|
resConfig := &Resolver{cveInfo: cveInfo, storeController: storeController, digestInfo: digestInfo, log: log}
|
|
|
|
return gql_generated.Config{
|
|
Resolvers: resConfig, Directives: gql_generated.DirectiveRoot{},
|
|
Complexity: gql_generated.ComplexityRoot{},
|
|
}
|
|
}
|
|
|
|
func (r *queryResolver) getImageListForDigest(repoList []string, digest string) ([]*gql_generated.ImageSummary, error) {
|
|
imgResultForDigest := []*gql_generated.ImageSummary{}
|
|
olu := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
|
|
|
var errResult error
|
|
|
|
for _, repo := range repoList {
|
|
r.log.Info().Str("repo", repo).Msg("filtering list of tags in image repo by digest")
|
|
|
|
imgTags, err := r.digestInfo.GetImageTagsByDigest(repo, digest)
|
|
if err != nil {
|
|
r.log.Error().Err(err).Msg("unable to get filtered list of image tags")
|
|
|
|
return []*gql_generated.ImageSummary{}, err
|
|
}
|
|
|
|
for _, imageInfo := range imgTags {
|
|
imageConfig, err := olu.GetImageConfigInfo(repo, imageInfo.Digest)
|
|
if err != nil {
|
|
return []*gql_generated.ImageSummary{}, err
|
|
}
|
|
|
|
imageInfo := BuildImageInfo(repo, imageInfo.Tag, imageInfo.Digest, imageInfo.Manifest, imageConfig)
|
|
|
|
imgResultForDigest = append(imgResultForDigest, imageInfo)
|
|
}
|
|
}
|
|
|
|
return imgResultForDigest, errResult
|
|
}
|
|
|
|
func repoListWithNewestImage(
|
|
ctx context.Context,
|
|
repoList []string,
|
|
olu common.OciLayoutUtils,
|
|
cveInfo cveinfo.CveInfo,
|
|
log log.Logger,
|
|
) ([]*gql_generated.RepoSummary, error) {
|
|
reposSummary := []*gql_generated.RepoSummary{}
|
|
|
|
for _, repo := range repoList {
|
|
lastUpdatedTag, err := olu.GetRepoLastUpdated(repo)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("can't get last updated manifest for repo: %s", repo)
|
|
log.Error().Err(err).Msg(msg)
|
|
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
|
|
continue
|
|
}
|
|
|
|
repoSize := int64(0)
|
|
repoBlob2Size := make(map[string]int64, 10)
|
|
|
|
manifests, err := olu.GetImageManifests(repo)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("can't get manifests for repo: %s", repo)
|
|
|
|
log.Error().Err(err).Msg(msg)
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
|
|
continue
|
|
}
|
|
|
|
repoPlatforms := make([]*gql_generated.OsArch, 0)
|
|
repoVendors := make([]*string, 0, len(manifests))
|
|
repoName := repo
|
|
|
|
var lastUpdatedImageSummary gql_generated.ImageSummary
|
|
|
|
var brokenManifest bool
|
|
|
|
for _, manifest := range manifests {
|
|
imageLayersSize := int64(0)
|
|
manifestSize := olu.GetImageManifestSize(repo, manifest.Digest)
|
|
|
|
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifest.Digest)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("reference not found for manifest %s", manifest.Digest)
|
|
|
|
log.Error().Err(err).Msg(msg)
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
|
|
brokenManifest = true
|
|
|
|
continue
|
|
}
|
|
|
|
configSize := imageBlobManifest.Config.Size
|
|
repoBlob2Size[manifest.Digest.String()] = manifestSize
|
|
repoBlob2Size[imageBlobManifest.Config.Digest.Hex] = configSize
|
|
|
|
for _, layer := range imageBlobManifest.Layers {
|
|
repoBlob2Size[layer.Digest.String()] = layer.Size
|
|
imageLayersSize += layer.Size
|
|
}
|
|
|
|
imageSize := imageLayersSize + manifestSize + configSize
|
|
|
|
imageConfigInfo, err := olu.GetImageConfigInfo(repo, manifest.Digest)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("can't get image config for manifest %s", manifest.Digest)
|
|
|
|
log.Error().Err(err).Msg(msg)
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
|
|
brokenManifest = true
|
|
|
|
continue
|
|
}
|
|
|
|
os, arch := olu.GetImagePlatform(imageConfigInfo)
|
|
osArch := &gql_generated.OsArch{
|
|
Os: &os,
|
|
Arch: &arch,
|
|
}
|
|
repoPlatforms = append(repoPlatforms, osArch)
|
|
|
|
// get image info from manifest annotation, if not found get from image config labels.
|
|
annotations := common.GetAnnotations(imageBlobManifest.Annotations, imageConfigInfo.Config.Labels)
|
|
|
|
repoVendors = append(repoVendors, &annotations.Vendor)
|
|
|
|
manifestTag, ok := manifest.Annotations[ispec.AnnotationRefName]
|
|
if !ok {
|
|
msg := fmt.Sprintf("reference not found for manifest %s in repo %s",
|
|
manifest.Digest.String(), repoName)
|
|
|
|
log.Error().Msg(msg)
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
|
|
brokenManifest = true
|
|
|
|
break
|
|
}
|
|
|
|
imageCveSummary := cveinfo.ImageCVESummary{}
|
|
// Check if vulnerability scanning is disabled
|
|
if cveInfo != nil {
|
|
imageName := fmt.Sprintf("%s:%s", repoName, manifestTag)
|
|
imageCveSummary, err = cveInfo.GetCVESummaryForImage(imageName)
|
|
|
|
if err != nil {
|
|
// Log the error, but we should still include the manifest in results
|
|
msg := fmt.Sprintf(
|
|
"unable to run vulnerability scan on tag %s in repo %s",
|
|
manifestTag,
|
|
repoName,
|
|
)
|
|
|
|
log.Error().Msg(msg)
|
|
graphql.AddError(ctx, gqlerror.Errorf(msg))
|
|
}
|
|
}
|
|
|
|
tag := manifestTag
|
|
size := strconv.Itoa(int(imageSize))
|
|
manifestDigest := manifest.Digest.Hex()
|
|
configDigest := imageBlobManifest.Config.Digest.Hex
|
|
isSigned := olu.CheckManifestSignature(repo, manifest.Digest)
|
|
lastUpdated := olu.GetImageLastUpdated(imageConfigInfo)
|
|
score := 0
|
|
|
|
imageSummary := gql_generated.ImageSummary{
|
|
RepoName: &repoName,
|
|
Tag: &tag,
|
|
LastUpdated: &lastUpdated,
|
|
Digest: &manifestDigest,
|
|
ConfigDigest: &configDigest,
|
|
IsSigned: &isSigned,
|
|
Size: &size,
|
|
Platform: osArch,
|
|
Vendor: &annotations.Vendor,
|
|
Score: &score,
|
|
Description: &annotations.Description,
|
|
Title: &annotations.Title,
|
|
Documentation: &annotations.Documentation,
|
|
Licenses: &annotations.Licenses,
|
|
Labels: &annotations.Labels,
|
|
Source: &annotations.Source,
|
|
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
|
|
MaxSeverity: &imageCveSummary.MaxSeverity,
|
|
Count: &imageCveSummary.Count,
|
|
},
|
|
}
|
|
|
|
if manifest.Digest.String() == lastUpdatedTag.Digest {
|
|
lastUpdatedImageSummary = imageSummary
|
|
}
|
|
}
|
|
|
|
if brokenManifest {
|
|
continue
|
|
}
|
|
|
|
for blob := range repoBlob2Size {
|
|
repoSize += repoBlob2Size[blob]
|
|
}
|
|
|
|
repoSizeStr := strconv.FormatInt(repoSize, 10)
|
|
index := 0
|
|
|
|
reposSummary = append(reposSummary, &gql_generated.RepoSummary{
|
|
Name: &repoName,
|
|
LastUpdated: &lastUpdatedTag.Timestamp,
|
|
Size: &repoSizeStr,
|
|
Platforms: repoPlatforms,
|
|
Vendors: repoVendors,
|
|
Score: &index,
|
|
NewestImage: &lastUpdatedImageSummary,
|
|
})
|
|
}
|
|
|
|
return reposSummary, 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,
|
|
cveInfo cveinfo.CveInfo, 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
|
|
repoBlob2Size := make(map[string]int64, 10)
|
|
|
|
// made up of all manifests, configs and image layers
|
|
repoSize := int64(0)
|
|
|
|
lastUpdatedTag, err := olu.GetRepoLastUpdated(repo)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("can't find latest updated tag for repo: %s", repo)
|
|
}
|
|
|
|
manifests, err := olu.GetImageManifests(repo)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("can't get manifests for repo: %s", repo)
|
|
|
|
continue
|
|
}
|
|
|
|
var lastUpdatedImageSummary gql_generated.ImageSummary
|
|
|
|
repoPlatforms := make([]*gql_generated.OsArch, 0, len(manifests))
|
|
repoVendors := make([]*string, 0, len(manifests))
|
|
|
|
for i, manifest := range manifests {
|
|
imageLayersSize := int64(0)
|
|
|
|
manifestTag, ok := manifest.Annotations[ispec.AnnotationRefName]
|
|
if !ok {
|
|
log.Error().Str("digest", manifest.Digest.String()).Msg("reference not found for this manifest")
|
|
|
|
continue
|
|
}
|
|
|
|
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifests[i].Digest)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("can't read manifest for repo %s %s", repo, manifestTag)
|
|
|
|
continue
|
|
}
|
|
|
|
manifestSize := olu.GetImageManifestSize(repo, manifest.Digest)
|
|
configSize := imageBlobManifest.Config.Size
|
|
|
|
repoBlob2Size[manifest.Digest.String()] = manifestSize
|
|
repoBlob2Size[imageBlobManifest.Config.Digest.Hex] = configSize
|
|
|
|
for _, layer := range imageBlobManifest.Layers {
|
|
layer := layer
|
|
layerDigest := layer.Digest.String()
|
|
layerSizeStr := strconv.Itoa(int(layer.Size))
|
|
repoBlob2Size[layer.Digest.String()] = layer.Size
|
|
imageLayersSize += layer.Size
|
|
|
|
// if we have a tag we won't match a layer
|
|
if tag != "" {
|
|
continue
|
|
}
|
|
|
|
if index := strings.Index(layerDigest, name); index != -1 {
|
|
layers = append(layers, &gql_generated.LayerSummary{
|
|
Digest: &layerDigest,
|
|
Size: &layerSizeStr,
|
|
Score: &index,
|
|
})
|
|
}
|
|
}
|
|
|
|
imageSize := imageLayersSize + manifestSize + configSize
|
|
|
|
index := strings.Index(repo, name)
|
|
matchesTag := strings.HasPrefix(manifestTag, tag)
|
|
|
|
if index != -1 {
|
|
imageConfigInfo, err := olu.GetImageConfigInfo(repo, manifests[i].Digest)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("can't retrieve config info for the image %s %s", repo, manifestTag)
|
|
|
|
continue
|
|
}
|
|
|
|
size := strconv.Itoa(int(imageSize))
|
|
isSigned := olu.CheckManifestSignature(repo, manifests[i].Digest)
|
|
|
|
// update matching score
|
|
score := calculateImageMatchingScore(repo, index, matchesTag)
|
|
|
|
lastUpdated := olu.GetImageLastUpdated(imageConfigInfo)
|
|
os, arch := olu.GetImagePlatform(imageConfigInfo)
|
|
osArch := &gql_generated.OsArch{
|
|
Os: &os,
|
|
Arch: &arch,
|
|
}
|
|
|
|
// get image info from manifest annotation, if not found get from image config labels.
|
|
annotations := common.GetAnnotations(imageBlobManifest.Annotations, imageConfigInfo.Config.Labels)
|
|
|
|
manifestDigest := manifest.Digest.Hex()
|
|
configDigest := imageBlobManifest.Config.Digest.Hex
|
|
|
|
repoPlatforms = append(repoPlatforms, osArch)
|
|
repoVendors = append(repoVendors, &annotations.Vendor)
|
|
|
|
imageCveSummary := cveinfo.ImageCVESummary{}
|
|
// Check if vulnerability scanning is disabled
|
|
if cveInfo != nil {
|
|
imageName := fmt.Sprintf("%s:%s", repo, manifestTag)
|
|
imageCveSummary, err = cveInfo.GetCVESummaryForImage(imageName)
|
|
|
|
if err != nil {
|
|
// Log the error, but we should still include the manifest in results
|
|
log.Error().Err(err).Msgf(
|
|
"unable to run vulnerability scan on tag %s in repo %s",
|
|
manifestTag,
|
|
repo,
|
|
)
|
|
}
|
|
}
|
|
|
|
imageSummary := gql_generated.ImageSummary{
|
|
RepoName: &repo,
|
|
Tag: &manifestTag,
|
|
LastUpdated: &lastUpdated,
|
|
Digest: &manifestDigest,
|
|
ConfigDigest: &configDigest,
|
|
IsSigned: &isSigned,
|
|
Size: &size,
|
|
Platform: osArch,
|
|
Vendor: &annotations.Vendor,
|
|
Score: &score,
|
|
Description: &annotations.Description,
|
|
Title: &annotations.Title,
|
|
Documentation: &annotations.Documentation,
|
|
Licenses: &annotations.Licenses,
|
|
Labels: &annotations.Labels,
|
|
Source: &annotations.Source,
|
|
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
|
|
MaxSeverity: &imageCveSummary.MaxSeverity,
|
|
Count: &imageCveSummary.Count,
|
|
},
|
|
}
|
|
|
|
if manifest.Digest.String() == lastUpdatedTag.Digest {
|
|
lastUpdatedImageSummary = imageSummary
|
|
}
|
|
|
|
images = append(images, &imageSummary)
|
|
}
|
|
}
|
|
|
|
for blob := range repoBlob2Size {
|
|
repoSize += repoBlob2Size[blob]
|
|
}
|
|
|
|
if index := strings.Index(repo, name); index != -1 {
|
|
repoSize := strconv.FormatInt(repoSize, 10)
|
|
|
|
repos = append(repos, &gql_generated.RepoSummary{
|
|
Name: &repo,
|
|
LastUpdated: &lastUpdatedTag.Timestamp,
|
|
Size: &repoSize,
|
|
Platforms: repoPlatforms,
|
|
Vendors: repoVendors,
|
|
Score: &index,
|
|
NewestImage: &lastUpdatedImageSummary,
|
|
})
|
|
}
|
|
}
|
|
|
|
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 (r *queryResolver) getImageList(store storage.ImageStore, imageName string) (
|
|
[]*gql_generated.ImageSummary, error,
|
|
) {
|
|
results := make([]*gql_generated.ImageSummary, 0)
|
|
|
|
repoList, err := store.GetRepositories()
|
|
if err != nil {
|
|
r.log.Error().Err(err).Msg("extension api: error extracting repositories list")
|
|
|
|
return results, err
|
|
}
|
|
|
|
layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
|
|
|
for _, repo := range repoList {
|
|
if (imageName != "" && repo == imageName) || imageName == "" {
|
|
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
|
|
if err != nil {
|
|
r.log.Error().Err(err).Msg("extension api: error getting tag timestamp info")
|
|
|
|
return results, nil
|
|
}
|
|
|
|
if len(tagsInfo) == 0 {
|
|
r.log.Info().Str("no tagsinfo found for repo", repo).Msg(" continuing traversing")
|
|
|
|
continue
|
|
}
|
|
|
|
for i := range tagsInfo {
|
|
// using a loop variable called tag would be reassigned after each iteration, using the same memory address
|
|
// directly access the value at the current index in the slice as ImageInfo requires pointers to tag fields
|
|
tag := tagsInfo[i]
|
|
|
|
digest := godigest.Digest(tag.Digest)
|
|
|
|
manifest, err := layoutUtils.GetImageBlobManifest(repo, digest)
|
|
if err != nil {
|
|
r.log.Error().Err(err).Msg("extension api: error reading manifest")
|
|
|
|
return results, err
|
|
}
|
|
|
|
imageConfig, err := layoutUtils.GetImageConfigInfo(repo, digest)
|
|
if err != nil {
|
|
return results, err
|
|
}
|
|
|
|
imageInfo := BuildImageInfo(repo, tag.Name, digest, manifest, imageConfig)
|
|
|
|
results = append(results, imageInfo)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
r.log.Info().Msg("no repositories found")
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func BuildImageInfo(repo string, tag string, manifestDigest godigest.Digest,
|
|
manifest v1.Manifest, imageConfig ispec.Image,
|
|
) *gql_generated.ImageSummary {
|
|
layers := []*gql_generated.LayerSummary{}
|
|
size := int64(0)
|
|
|
|
log := log.NewLogger("debug", "")
|
|
|
|
allHistory := []*gql_generated.LayerHistory{}
|
|
|
|
formattedManifestDigest := manifestDigest.Hex()
|
|
|
|
history := imageConfig.History
|
|
if len(history) == 0 {
|
|
for _, layer := range manifest.Layers {
|
|
size += layer.Size
|
|
digest := layer.Digest.Hex
|
|
layerSize := strconv.FormatInt(layer.Size, 10)
|
|
|
|
layer := &gql_generated.LayerSummary{
|
|
Size: &layerSize,
|
|
Digest: &digest,
|
|
}
|
|
|
|
layers = append(
|
|
layers,
|
|
layer,
|
|
)
|
|
|
|
allHistory = append(allHistory, &gql_generated.LayerHistory{
|
|
Layer: layer,
|
|
HistoryDescription: &gql_generated.HistoryDescription{},
|
|
})
|
|
}
|
|
|
|
formattedSize := strconv.FormatInt(size, 10)
|
|
|
|
imageInfo := &gql_generated.ImageSummary{
|
|
RepoName: &repo,
|
|
Tag: &tag,
|
|
Digest: &formattedManifestDigest,
|
|
ConfigDigest: &manifest.Config.Digest.Hex,
|
|
Size: &formattedSize,
|
|
Layers: layers,
|
|
History: []*gql_generated.LayerHistory{},
|
|
}
|
|
|
|
return imageInfo
|
|
}
|
|
|
|
// iterator over manifest layers
|
|
var layersIterator int
|
|
// since we are appending pointers, it is important to iterate with an index over slice
|
|
for i := range history {
|
|
allHistory = append(allHistory, &gql_generated.LayerHistory{
|
|
HistoryDescription: &gql_generated.HistoryDescription{
|
|
Created: history[i].Created,
|
|
CreatedBy: &history[i].CreatedBy,
|
|
Author: &history[i].Author,
|
|
Comment: &history[i].Comment,
|
|
EmptyLayer: &history[i].EmptyLayer,
|
|
},
|
|
})
|
|
|
|
if history[i].EmptyLayer {
|
|
continue
|
|
}
|
|
|
|
if layersIterator+1 > len(manifest.Layers) {
|
|
formattedSize := strconv.FormatInt(size, 10)
|
|
|
|
log.Error().Err(ErrBadLayerCount).Msg("error on creating layer history for ImageSummary")
|
|
|
|
return &gql_generated.ImageSummary{
|
|
RepoName: &repo,
|
|
Tag: &tag,
|
|
Digest: &formattedManifestDigest,
|
|
ConfigDigest: &manifest.Config.Digest.Hex,
|
|
Size: &formattedSize,
|
|
Layers: layers,
|
|
History: allHistory,
|
|
}
|
|
}
|
|
|
|
size += manifest.Layers[layersIterator].Size
|
|
digest := manifest.Layers[layersIterator].Digest.Hex
|
|
layerSize := strconv.FormatInt(manifest.Layers[layersIterator].Size, 10)
|
|
|
|
layer := &gql_generated.LayerSummary{
|
|
Size: &layerSize,
|
|
Digest: &digest,
|
|
}
|
|
|
|
layers = append(
|
|
layers,
|
|
layer,
|
|
)
|
|
|
|
allHistory[i].Layer = layer
|
|
|
|
layersIterator++
|
|
}
|
|
|
|
formattedSize := strconv.FormatInt(size, 10)
|
|
|
|
imageInfo := &gql_generated.ImageSummary{
|
|
RepoName: &repo,
|
|
Tag: &tag,
|
|
Digest: &formattedManifestDigest,
|
|
ConfigDigest: &manifest.Config.Digest.Hex,
|
|
Size: &formattedSize,
|
|
Layers: layers,
|
|
History: allHistory,
|
|
}
|
|
|
|
return imageInfo
|
|
}
|
|
|
|
// returns either a user has or not rights on 'repository'.
|
|
func matchesRepo(globPatterns map[string]bool, repository string) bool {
|
|
var longestMatchedPattern string
|
|
|
|
// because of the longest path matching rule, we need to check all patterns from config
|
|
for pattern := range globPatterns {
|
|
matched, err := glob.Match(pattern, repository)
|
|
if err == nil {
|
|
if matched && len(pattern) > len(longestMatchedPattern) {
|
|
longestMatchedPattern = pattern
|
|
}
|
|
}
|
|
}
|
|
|
|
allowed := globPatterns[longestMatchedPattern]
|
|
|
|
return allowed
|
|
}
|
|
|
|
// get passed context from authzHandler and filter out repos based on permissions.
|
|
func userAvailableRepos(ctx context.Context, repoList []string) ([]string, error) {
|
|
var availableRepos []string
|
|
|
|
authzCtxKey := localCtx.GetContextKey()
|
|
if authCtx := ctx.Value(authzCtxKey); authCtx != nil {
|
|
acCtx, ok := authCtx.(localCtx.AccessControlContext)
|
|
if !ok {
|
|
err := ErrBadCtxFormat
|
|
|
|
return []string{}, err
|
|
}
|
|
|
|
for _, r := range repoList {
|
|
if acCtx.IsAdmin || matchesRepo(acCtx.GlobPatterns, r) {
|
|
availableRepos = append(availableRepos, r)
|
|
}
|
|
}
|
|
} else {
|
|
availableRepos = repoList
|
|
}
|
|
|
|
return availableRepos, nil
|
|
}
|