mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
feat(search): add artifact type to manifest summary gql structure (#1448)
Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
parent
912854f29b
commit
f4501e6b6b
6 changed files with 202 additions and 3 deletions
|
@ -53,6 +53,8 @@ type ManifestSummary struct {
|
||||||
Layers []LayerSummary `json:"layers"`
|
Layers []LayerSummary `json:"layers"`
|
||||||
History []LayerHistory `json:"history"`
|
History []LayerHistory `json:"history"`
|
||||||
Vulnerabilities ImageVulnerabilitySummary `json:"vulnerabilities"`
|
Vulnerabilities ImageVulnerabilitySummary `json:"vulnerabilities"`
|
||||||
|
Referrers []Referrer `json:"referrers"`
|
||||||
|
ArtifactType string `json:"artifactType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Platform struct {
|
type Platform struct {
|
||||||
|
|
|
@ -301,6 +301,7 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
|
||||||
repoName = repo
|
repoName = repo
|
||||||
configDigest = manifestContent.Config.Digest.String()
|
configDigest = manifestContent.Config.Digest.String()
|
||||||
configSize = manifestContent.Config.Size
|
configSize = manifestContent.Config.Size
|
||||||
|
artifactType = common.GetManifestArtifactType(manifestContent)
|
||||||
imageLastUpdated = common.GetImageLastUpdated(configContent)
|
imageLastUpdated = common.GetImageLastUpdated(configContent)
|
||||||
downloadCount = repoMeta.Statistics[digest.String()].DownloadCount
|
downloadCount = repoMeta.Statistics[digest.String()].DownloadCount
|
||||||
isSigned = false
|
isSigned = false
|
||||||
|
@ -373,6 +374,8 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
|
||||||
MaxSeverity: &imageCveSummary.MaxSeverity,
|
MaxSeverity: &imageCveSummary.MaxSeverity,
|
||||||
Count: &imageCveSummary.Count,
|
Count: &imageCveSummary.Count,
|
||||||
},
|
},
|
||||||
|
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
|
||||||
|
ArtifactType: &artifactType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LastUpdated: &imageLastUpdated,
|
LastUpdated: &imageLastUpdated,
|
||||||
|
@ -437,7 +440,6 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
|
||||||
) (*gql_generated.ManifestSummary, map[string]int64, error) {
|
) (*gql_generated.ManifestSummary, map[string]int64, error) {
|
||||||
var (
|
var (
|
||||||
manifestContent ispec.Manifest
|
manifestContent ispec.Manifest
|
||||||
|
|
||||||
digest = descriptor.Digest
|
digest = descriptor.Digest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -463,6 +465,7 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
|
||||||
manifestDigestStr = digest.String()
|
manifestDigestStr = digest.String()
|
||||||
configDigest = manifestContent.Config.Digest.String()
|
configDigest = manifestContent.Config.Digest.String()
|
||||||
configSize = manifestContent.Config.Size
|
configSize = manifestContent.Config.Size
|
||||||
|
artifactType = common.GetManifestArtifactType(manifestContent)
|
||||||
imageLastUpdated = common.GetImageLastUpdated(configContent)
|
imageLastUpdated = common.GetImageLastUpdated(configContent)
|
||||||
downloadCount = manifestMeta.DownloadCount
|
downloadCount = manifestMeta.DownloadCount
|
||||||
isSigned = false
|
isSigned = false
|
||||||
|
@ -523,6 +526,7 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
|
||||||
Count: &imageCveSummary.Count,
|
Count: &imageCveSummary.Count,
|
||||||
},
|
},
|
||||||
Referrers: getReferrers(referrersInfo),
|
Referrers: getReferrers(referrersInfo),
|
||||||
|
ArtifactType: &artifactType,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &manifestSummary, imageBlobsMap, nil
|
return &manifestSummary, imageBlobsMap, nil
|
||||||
|
|
|
@ -115,6 +115,7 @@ type ComplexityRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
ManifestSummary struct {
|
ManifestSummary struct {
|
||||||
|
ArtifactType func(childComplexity int) int
|
||||||
ConfigDigest func(childComplexity int) int
|
ConfigDigest func(childComplexity int) int
|
||||||
Digest func(childComplexity int) int
|
Digest func(childComplexity int) int
|
||||||
DownloadCount func(childComplexity int) int
|
DownloadCount func(childComplexity int) int
|
||||||
|
@ -538,6 +539,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.LayerSummary.Size(childComplexity), true
|
return e.complexity.LayerSummary.Size(childComplexity), true
|
||||||
|
|
||||||
|
case "ManifestSummary.ArtifactType":
|
||||||
|
if e.complexity.ManifestSummary.ArtifactType == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.ManifestSummary.ArtifactType(childComplexity), true
|
||||||
|
|
||||||
case "ManifestSummary.ConfigDigest":
|
case "ManifestSummary.ConfigDigest":
|
||||||
if e.complexity.ManifestSummary.ConfigDigest == nil {
|
if e.complexity.ManifestSummary.ConfigDigest == nil {
|
||||||
break
|
break
|
||||||
|
@ -1278,6 +1286,10 @@ type ManifestSummary {
|
||||||
Information about objects that reference this image
|
Information about objects that reference this image
|
||||||
"""
|
"""
|
||||||
Referrers: [Referrer]
|
Referrers: [Referrer]
|
||||||
|
"""
|
||||||
|
Value of the artifactType field if present else the value of the config media type
|
||||||
|
"""
|
||||||
|
ArtifactType: String
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -3248,6 +3260,8 @@ func (ec *executionContext) fieldContext_ImageSummary_Manifests(ctx context.Cont
|
||||||
return ec.fieldContext_ManifestSummary_Vulnerabilities(ctx, field)
|
return ec.fieldContext_ManifestSummary_Vulnerabilities(ctx, field)
|
||||||
case "Referrers":
|
case "Referrers":
|
||||||
return ec.fieldContext_ManifestSummary_Referrers(ctx, field)
|
return ec.fieldContext_ManifestSummary_Referrers(ctx, field)
|
||||||
|
case "ArtifactType":
|
||||||
|
return ec.fieldContext_ManifestSummary_ArtifactType(ctx, field)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no field named %q was found under type ManifestSummary", field.Name)
|
return nil, fmt.Errorf("no field named %q was found under type ManifestSummary", field.Name)
|
||||||
},
|
},
|
||||||
|
@ -4598,6 +4612,47 @@ func (ec *executionContext) fieldContext_ManifestSummary_Referrers(ctx context.C
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _ManifestSummary_ArtifactType(ctx context.Context, field graphql.CollectedField, obj *ManifestSummary) (ret graphql.Marshaler) {
|
||||||
|
fc, err := ec.fieldContext_ManifestSummary_ArtifactType(ctx, field)
|
||||||
|
if err != nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.ArtifactType, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_ManifestSummary_ArtifactType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "ManifestSummary",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
return nil, errors.New("field of type String does not have child fields")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _PackageInfo_Name(ctx context.Context, field graphql.CollectedField, obj *PackageInfo) (ret graphql.Marshaler) {
|
func (ec *executionContext) _PackageInfo_Name(ctx context.Context, field graphql.CollectedField, obj *PackageInfo) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_PackageInfo_Name(ctx, field)
|
fc, err := ec.fieldContext_PackageInfo_Name(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9282,6 +9337,10 @@ func (ec *executionContext) _ManifestSummary(ctx context.Context, sel ast.Select
|
||||||
|
|
||||||
out.Values[i] = ec._ManifestSummary_Referrers(ctx, field, obj)
|
out.Values[i] = ec._ManifestSummary_Referrers(ctx, field, obj)
|
||||||
|
|
||||||
|
case "ArtifactType":
|
||||||
|
|
||||||
|
out.Values[i] = ec._ManifestSummary_ArtifactType(ctx, field, obj)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,8 @@ type ManifestSummary struct {
|
||||||
Vulnerabilities *ImageVulnerabilitySummary `json:"Vulnerabilities,omitempty"`
|
Vulnerabilities *ImageVulnerabilitySummary `json:"Vulnerabilities,omitempty"`
|
||||||
// Information about objects that reference this image
|
// Information about objects that reference this image
|
||||||
Referrers []*Referrer `json:"Referrers,omitempty"`
|
Referrers []*Referrer `json:"Referrers,omitempty"`
|
||||||
|
// Value of the artifactType field if present else the value of the config media type
|
||||||
|
ArtifactType *string `json:"ArtifactType,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains the name of the package, the current installed version and the version where the CVE was fixed
|
// Contains the name of the package, the current installed version and the version where the CVE was fixed
|
||||||
|
|
|
@ -242,6 +242,10 @@ type ManifestSummary {
|
||||||
Information about objects that reference this image
|
Information about objects that reference this image
|
||||||
"""
|
"""
|
||||||
Referrers: [Referrer]
|
Referrers: [Referrer]
|
||||||
|
"""
|
||||||
|
Value of the artifactType field if present else the value of the config media type
|
||||||
|
"""
|
||||||
|
ArtifactType: String
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -6456,4 +6456,132 @@ func TestImageSummary(t *testing.T) {
|
||||||
// There are 0 vulnerabilities this data used in tests
|
// There are 0 vulnerabilities this data used in tests
|
||||||
So(imgSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
|
So(imgSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "CRITICAL")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("GraphQL query for Artifact Type", t, func() {
|
||||||
|
port := GetFreePort()
|
||||||
|
baseURL := GetBaseURL(port)
|
||||||
|
conf := config.New()
|
||||||
|
conf.HTTP.Port = port
|
||||||
|
conf.Storage.RootDirectory = t.TempDir()
|
||||||
|
conf.Storage.GC = false
|
||||||
|
|
||||||
|
defaultVal := true
|
||||||
|
conf.Extensions = &extconf.ExtensionConfig{
|
||||||
|
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Extensions.Search.CVE = nil
|
||||||
|
|
||||||
|
ctlr := api.NewController(conf)
|
||||||
|
|
||||||
|
query := `
|
||||||
|
{
|
||||||
|
Image(image:"repo:art%d"){
|
||||||
|
RepoName
|
||||||
|
Tag
|
||||||
|
Manifests {
|
||||||
|
Digest
|
||||||
|
ArtifactType
|
||||||
|
}
|
||||||
|
Size
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
queryImg1 := fmt.Sprintf(query, 1)
|
||||||
|
queryImg2 := fmt.Sprintf(query, 2)
|
||||||
|
|
||||||
|
var imgSummaryResponse ImageSummaryResult
|
||||||
|
|
||||||
|
ctlrManager := NewControllerManager(ctlr)
|
||||||
|
ctlrManager.StartAndWait(port)
|
||||||
|
defer ctlrManager.StopServer()
|
||||||
|
|
||||||
|
// upload the images
|
||||||
|
artType1 := "application/test.signature.v1"
|
||||||
|
artType2 := "application/test.signature.v2"
|
||||||
|
|
||||||
|
img1, err := GetRandomImage("art1")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
img1.Manifest.Config = ispec.ScratchDescriptor
|
||||||
|
img1.Manifest.ArtifactType = artType1
|
||||||
|
digest1, err := img1.Digest()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = UploadImage(img1, baseURL, "repo")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
img2, err := GetRandomImage("art2")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
img2.Manifest.Config.MediaType = artType2
|
||||||
|
digest2, err := img2.Digest()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = UploadImage(img2, baseURL, "repo")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
// GET image 1
|
||||||
|
resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(queryImg1))
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
So(resp.Body(), ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &imgSummaryResponse)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
imgSum := imgSummaryResponse.SingleImageSummary.ImageSummary
|
||||||
|
So(len(imgSum.Manifests), ShouldEqual, 1)
|
||||||
|
So(imgSum.Manifests[0].ArtifactType, ShouldResemble, artType1)
|
||||||
|
|
||||||
|
// GET image 2
|
||||||
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(queryImg2))
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
So(resp.Body(), ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &imgSummaryResponse)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
imgSum = imgSummaryResponse.SingleImageSummary.ImageSummary
|
||||||
|
So(len(imgSum.Manifests), ShouldEqual, 1)
|
||||||
|
So(imgSum.Manifests[0].ArtifactType, ShouldResemble, artType2)
|
||||||
|
|
||||||
|
// Expanded repo info test
|
||||||
|
|
||||||
|
queryExpRepoInfo := `{
|
||||||
|
ExpandedRepoInfo(repo:"test1"){
|
||||||
|
Images {
|
||||||
|
Tag
|
||||||
|
Manifests {
|
||||||
|
Digest
|
||||||
|
ArtifactType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var expandedRepoInfoResp ExpandedRepoInfoResp
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" +
|
||||||
|
url.QueryEscape(queryExpRepoInfo))
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
So(resp.Body(), ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &expandedRepoInfoResp)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
imgSums := expandedRepoInfoResp.ExpandedRepoInfo.RepoInfo.ImageSummaries
|
||||||
|
|
||||||
|
for _, imgSum := range imgSums {
|
||||||
|
switch imgSum.Digest {
|
||||||
|
case digest1.String():
|
||||||
|
So(imgSum.Manifests[0].ArtifactType, ShouldResemble, artType1)
|
||||||
|
case digest2.String():
|
||||||
|
So(imgSum.Manifests[0].ArtifactType, ShouldResemble, artType2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue