mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
feat(graphql): add an api to return referrers (#1009)
UI can now make use of OCI artifacts and references using `Referrers` gQL query. It returns a list of descriptors that refer on their `subject` field to another digest. Signed-off-by: Alex Stan <alexandrustan96@yahoo.ro>
This commit is contained in:
parent
8746a49268
commit
f75bce3085
10 changed files with 1148 additions and 0 deletions
|
@ -76,6 +76,14 @@ type ExpandedRepoInfoResp struct {
|
|||
Errors []ErrorGQL `json:"errors"`
|
||||
}
|
||||
|
||||
type ReferrersResp struct {
|
||||
ReferrersResult ReferrersResult `json:"data"`
|
||||
Errors []ErrorGQL `json:"errors"`
|
||||
}
|
||||
|
||||
type ReferrersResult struct {
|
||||
Referrers []common.Referrer `json:"referrers"`
|
||||
}
|
||||
type GlobalSearchResultResp struct {
|
||||
GlobalSearchResult GlobalSearchResult `json:"data"`
|
||||
Errors []ErrorGQL `json:"errors"`
|
||||
|
@ -658,6 +666,138 @@ func TestRepoListWithNewestImage(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGetReferrersGQL(t *testing.T) {
|
||||
Convey("get referrers", t, func() {
|
||||
port := GetFreePort()
|
||||
baseURL := GetBaseURL(port)
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = t.TempDir()
|
||||
|
||||
defaultVal := true
|
||||
conf.Extensions = &extconf.ExtensionConfig{
|
||||
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
|
||||
Lint: &extconf.LintConfig{
|
||||
BaseConfig: extconf.BaseConfig{
|
||||
Enable: &defaultVal,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
gqlEndpoint := fmt.Sprintf("%s%s?query=", baseURL, graphqlQueryPrefix)
|
||||
|
||||
conf.Extensions.Search.CVE = nil
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
|
||||
WaitTillServerReady(baseURL)
|
||||
|
||||
// =======================
|
||||
|
||||
config, layers, manifest, err := GetImageComponents(1000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
repo := "artifact-ref"
|
||||
|
||||
err = UploadImage(
|
||||
Image{
|
||||
Manifest: manifest,
|
||||
Config: config,
|
||||
Layers: layers,
|
||||
Tag: "1.0",
|
||||
},
|
||||
baseURL,
|
||||
repo)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestBlob, err := json.Marshal(manifest)
|
||||
So(err, ShouldBeNil)
|
||||
manifestDigest := godigest.FromBytes(manifestBlob)
|
||||
manifestSize := int64(len(manifestBlob))
|
||||
|
||||
subjectDescriptor := &ispec.Descriptor{
|
||||
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
Size: manifestSize,
|
||||
Digest: manifestDigest,
|
||||
}
|
||||
|
||||
artifactContentBlob := []byte("test artifact")
|
||||
artifactContentBlobSize := int64(len(artifactContentBlob))
|
||||
artifactContentType := "application/octet-stream"
|
||||
artifactContentBlobDigest := godigest.FromBytes(artifactContentBlob)
|
||||
artifactType := "com.artifact.test"
|
||||
|
||||
err = UploadBlob(baseURL, repo, artifactContentBlob, artifactContentType)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
artifact := &ispec.Artifact{
|
||||
Blobs: []ispec.Descriptor{
|
||||
{
|
||||
MediaType: artifactContentType,
|
||||
Digest: artifactContentBlobDigest,
|
||||
Size: artifactContentBlobSize,
|
||||
},
|
||||
},
|
||||
Subject: subjectDescriptor,
|
||||
ArtifactType: artifactType,
|
||||
Annotations: map[string]string{
|
||||
"com.artifact.format": "test",
|
||||
},
|
||||
}
|
||||
|
||||
artifactManifestBlob, err := json.Marshal(artifact)
|
||||
So(err, ShouldBeNil)
|
||||
artifactManifestDigest := godigest.FromBytes(artifactManifestBlob)
|
||||
|
||||
err = UploadArtifact(baseURL, repo, artifact)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
gqlQuery := `
|
||||
{Referrers(
|
||||
repo: "%s",
|
||||
digest: "%s",
|
||||
type: ""
|
||||
){
|
||||
ArtifactType,
|
||||
Digest,
|
||||
MediaType,
|
||||
Size,
|
||||
Annotations{
|
||||
Key
|
||||
Value
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
strQuery := fmt.Sprintf(gqlQuery, repo, manifestDigest.String())
|
||||
|
||||
targetURL := fmt.Sprintf("%s%s", gqlEndpoint, url.QueryEscape(strQuery))
|
||||
|
||||
resp, err := resty.R().Get(targetURL)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.Body(), ShouldNotBeNil)
|
||||
|
||||
refferrsResp := &ReferrersResp{}
|
||||
|
||||
err = json.Unmarshal(resp.Body(), refferrsResp)
|
||||
So(err, ShouldBeNil)
|
||||
So(refferrsResp.Errors, ShouldBeNil)
|
||||
So(refferrsResp.ReferrersResult.Referrers[0].ArtifactType, ShouldEqual, artifactType)
|
||||
So(refferrsResp.ReferrersResult.Referrers[0].MediaType, ShouldEqual, ispec.MediaTypeArtifactManifest)
|
||||
|
||||
So(refferrsResp.ReferrersResult.Referrers[0].Annotations[0].Key, ShouldEqual, "com.artifact.format")
|
||||
So(refferrsResp.ReferrersResult.Referrers[0].Annotations[0].Value, ShouldEqual, "test")
|
||||
|
||||
So(refferrsResp.ReferrersResult.Referrers[0].Digest, ShouldEqual, artifactManifestDigest)
|
||||
})
|
||||
}
|
||||
|
||||
func TestExpandedRepoInfo(t *testing.T) {
|
||||
Convey("Filter out manifests with no tag", t, func() {
|
||||
tagToBeRemoved := "3.0"
|
||||
|
|
|
@ -71,3 +71,16 @@ type HistoryDescription struct {
|
|||
Comment string `json:"comment"`
|
||||
EmptyLayer bool `json:"emptyLayer"`
|
||||
}
|
||||
|
||||
type Referrer struct {
|
||||
MediaType string `json:"mediatype"`
|
||||
ArtifactType string `json:"artifacttype"`
|
||||
Size int `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
Annotations []Annotation `json:"annotations"`
|
||||
}
|
||||
|
||||
type Annotation struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
|
|
@ -43,6 +43,11 @@ type DirectiveRoot struct {
|
|||
}
|
||||
|
||||
type ComplexityRoot struct {
|
||||
Annotation struct {
|
||||
Key func(childComplexity int) int
|
||||
Value func(childComplexity int) int
|
||||
}
|
||||
|
||||
CVE struct {
|
||||
Description func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
|
@ -132,9 +137,18 @@ type ComplexityRoot struct {
|
|||
ImageListForCve func(childComplexity int, id string) int
|
||||
ImageListForDigest func(childComplexity int, id string) int
|
||||
ImageListWithCVEFixed func(childComplexity int, id string, image string) int
|
||||
Referrers func(childComplexity int, repo string, digest string, typeArg string) int
|
||||
RepoListWithNewestImage func(childComplexity int) int
|
||||
}
|
||||
|
||||
Referrer struct {
|
||||
Annotations func(childComplexity int) int
|
||||
ArtifactType func(childComplexity int) int
|
||||
Digest func(childComplexity int) int
|
||||
MediaType func(childComplexity int) int
|
||||
Size func(childComplexity int) int
|
||||
}
|
||||
|
||||
RepoInfo struct {
|
||||
Images func(childComplexity int) int
|
||||
Summary func(childComplexity int) int
|
||||
|
@ -166,6 +180,7 @@ type QueryResolver interface {
|
|||
DerivedImageList(ctx context.Context, image string) ([]*ImageSummary, error)
|
||||
BaseImageList(ctx context.Context, image string) ([]*ImageSummary, error)
|
||||
Image(ctx context.Context, image string) (*ImageSummary, error)
|
||||
Referrers(ctx context.Context, repo string, digest string, typeArg string) ([]*Referrer, error)
|
||||
}
|
||||
|
||||
type executableSchema struct {
|
||||
|
@ -183,6 +198,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
_ = ec
|
||||
switch typeName + "." + field {
|
||||
|
||||
case "Annotation.Key":
|
||||
if e.complexity.Annotation.Key == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Annotation.Key(childComplexity), true
|
||||
|
||||
case "Annotation.Value":
|
||||
if e.complexity.Annotation.Value == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Annotation.Value(childComplexity), true
|
||||
|
||||
case "CVE.Description":
|
||||
if e.complexity.CVE.Description == nil {
|
||||
break
|
||||
|
@ -639,6 +668,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Query.ImageListWithCVEFixed(childComplexity, args["id"].(string), args["image"].(string)), true
|
||||
|
||||
case "Query.Referrers":
|
||||
if e.complexity.Query.Referrers == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Query_Referrers_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.Referrers(childComplexity, args["repo"].(string), args["digest"].(string), args["type"].(string)), true
|
||||
|
||||
case "Query.RepoListWithNewestImage":
|
||||
if e.complexity.Query.RepoListWithNewestImage == nil {
|
||||
break
|
||||
|
@ -646,6 +687,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Query.RepoListWithNewestImage(childComplexity), true
|
||||
|
||||
case "Referrer.Annotations":
|
||||
if e.complexity.Referrer.Annotations == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Referrer.Annotations(childComplexity), true
|
||||
|
||||
case "Referrer.ArtifactType":
|
||||
if e.complexity.Referrer.ArtifactType == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Referrer.ArtifactType(childComplexity), true
|
||||
|
||||
case "Referrer.Digest":
|
||||
if e.complexity.Referrer.Digest == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Referrer.Digest(childComplexity), true
|
||||
|
||||
case "Referrer.MediaType":
|
||||
if e.complexity.Referrer.MediaType == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Referrer.MediaType(childComplexity), true
|
||||
|
||||
case "Referrer.Size":
|
||||
if e.complexity.Referrer.Size == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Referrer.Size(childComplexity), true
|
||||
|
||||
case "RepoInfo.Images":
|
||||
if e.complexity.RepoInfo.Images == nil {
|
||||
break
|
||||
|
@ -918,6 +994,19 @@ type LayerHistory {
|
|||
HistoryDescription: HistoryDescription
|
||||
}
|
||||
|
||||
type Annotation {
|
||||
Key: String
|
||||
Value: String
|
||||
}
|
||||
|
||||
type Referrer {
|
||||
MediaType: String
|
||||
ArtifactType: String
|
||||
Size: Int
|
||||
Digest: String
|
||||
Annotations: [Annotation]!
|
||||
}
|
||||
|
||||
"""
|
||||
Contains details about the supported OS and architecture of the image
|
||||
"""
|
||||
|
@ -981,6 +1070,12 @@ type Query {
|
|||
Search for a specific image using its name
|
||||
"""
|
||||
Image(image: String!): ImageSummary
|
||||
|
||||
"""
|
||||
Returns a list of descriptors of an image or artifact manifest that are found in a <repo> and have a subject field of <digest>
|
||||
Can be filtered based on a specific artifact type <type>
|
||||
"""
|
||||
Referrers(repo: String!, digest: String!, type: String!): [Referrer]!
|
||||
}
|
||||
`, BuiltIn: false},
|
||||
}
|
||||
|
@ -1149,6 +1244,39 @@ func (ec *executionContext) field_Query_Image_args(ctx context.Context, rawArgs
|
|||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_Referrers_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 string
|
||||
if tmp, ok := rawArgs["repo"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("repo"))
|
||||
arg0, err = ec.unmarshalNString2string(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["repo"] = arg0
|
||||
var arg1 string
|
||||
if tmp, ok := rawArgs["digest"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("digest"))
|
||||
arg1, err = ec.unmarshalNString2string(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["digest"] = arg1
|
||||
var arg2 string
|
||||
if tmp, ok := rawArgs["type"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type"))
|
||||
arg2, err = ec.unmarshalNString2string(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["type"] = arg2
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
|
@ -1202,6 +1330,88 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
|
|||
|
||||
// region **************************** field.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) _Annotation_Key(ctx context.Context, field graphql.CollectedField, obj *Annotation) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Annotation_Key(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.Key, 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_Annotation_Key(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Annotation",
|
||||
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) _Annotation_Value(ctx context.Context, field graphql.CollectedField, obj *Annotation) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Annotation_Value(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.Value, 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_Annotation_Value(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Annotation",
|
||||
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) _CVE_Id(ctx context.Context, field graphql.CollectedField, obj *Cve) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_CVE_Id(ctx, field)
|
||||
if err != nil {
|
||||
|
@ -4233,6 +4443,73 @@ func (ec *executionContext) fieldContext_Query_Image(ctx context.Context, field
|
|||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_Referrers(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query_Referrers(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 ec.resolvers.Query().Referrers(rctx, fc.Args["repo"].(string), fc.Args["digest"].(string), fc.Args["type"].(string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]*Referrer)
|
||||
fc.Result = res
|
||||
return ec.marshalNReferrer2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐReferrer(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Query_Referrers(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "MediaType":
|
||||
return ec.fieldContext_Referrer_MediaType(ctx, field)
|
||||
case "ArtifactType":
|
||||
return ec.fieldContext_Referrer_ArtifactType(ctx, field)
|
||||
case "Size":
|
||||
return ec.fieldContext_Referrer_Size(ctx, field)
|
||||
case "Digest":
|
||||
return ec.fieldContext_Referrer_Digest(ctx, field)
|
||||
case "Annotations":
|
||||
return ec.fieldContext_Referrer_Annotations(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type Referrer", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = ec.Recover(ctx, r)
|
||||
ec.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
if fc.Args, err = ec.field_Query_Referrers_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Query___type(ctx, field)
|
||||
if err != nil {
|
||||
|
@ -4362,6 +4639,220 @@ func (ec *executionContext) fieldContext_Query___schema(ctx context.Context, fie
|
|||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Referrer_MediaType(ctx context.Context, field graphql.CollectedField, obj *Referrer) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Referrer_MediaType(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.MediaType, 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_Referrer_MediaType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Referrer",
|
||||
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) _Referrer_ArtifactType(ctx context.Context, field graphql.CollectedField, obj *Referrer) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Referrer_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_Referrer_ArtifactType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Referrer",
|
||||
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) _Referrer_Size(ctx context.Context, field graphql.CollectedField, obj *Referrer) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Referrer_Size(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.Size, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*int)
|
||||
fc.Result = res
|
||||
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Referrer_Size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Referrer",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Int does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Referrer_Digest(ctx context.Context, field graphql.CollectedField, obj *Referrer) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Referrer_Digest(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.Digest, 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_Referrer_Digest(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Referrer",
|
||||
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) _Referrer_Annotations(ctx context.Context, field graphql.CollectedField, obj *Referrer) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Referrer_Annotations(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.Annotations, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.([]*Annotation)
|
||||
fc.Result = res
|
||||
return ec.marshalNAnnotation2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐAnnotation(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Referrer_Annotations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Referrer",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "Key":
|
||||
return ec.fieldContext_Annotation_Key(ctx, field)
|
||||
case "Value":
|
||||
return ec.fieldContext_Annotation_Value(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type Annotation", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _RepoInfo_Images(ctx context.Context, field graphql.CollectedField, obj *RepoInfo) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_RepoInfo_Images(ctx, field)
|
||||
if err != nil {
|
||||
|
@ -6751,6 +7242,35 @@ func (ec *executionContext) fieldContext___Type_specifiedByURL(ctx context.Conte
|
|||
|
||||
// region **************************** object.gotpl ****************************
|
||||
|
||||
var annotationImplementors = []string{"Annotation"}
|
||||
|
||||
func (ec *executionContext) _Annotation(ctx context.Context, sel ast.SelectionSet, obj *Annotation) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, annotationImplementors)
|
||||
out := graphql.NewFieldSet(fields)
|
||||
var invalids uint32
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Annotation")
|
||||
case "Key":
|
||||
|
||||
out.Values[i] = ec._Annotation_Key(ctx, field, obj)
|
||||
|
||||
case "Value":
|
||||
|
||||
out.Values[i] = ec._Annotation_Value(ctx, field, obj)
|
||||
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch()
|
||||
if invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var cVEImplementors = []string{"CVE"}
|
||||
|
||||
func (ec *executionContext) _CVE(ctx context.Context, sel ast.SelectionSet, obj *Cve) graphql.Marshaler {
|
||||
|
@ -7401,6 +7921,29 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
|||
return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)
|
||||
}
|
||||
|
||||
out.Concurrently(i, func() graphql.Marshaler {
|
||||
return rrm(innerCtx)
|
||||
})
|
||||
case "Referrers":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_Referrers(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
rrm := func(ctx context.Context) graphql.Marshaler {
|
||||
return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc)
|
||||
}
|
||||
|
||||
out.Concurrently(i, func() graphql.Marshaler {
|
||||
return rrm(innerCtx)
|
||||
})
|
||||
|
@ -7427,6 +7970,50 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
|||
return out
|
||||
}
|
||||
|
||||
var referrerImplementors = []string{"Referrer"}
|
||||
|
||||
func (ec *executionContext) _Referrer(ctx context.Context, sel ast.SelectionSet, obj *Referrer) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, referrerImplementors)
|
||||
out := graphql.NewFieldSet(fields)
|
||||
var invalids uint32
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Referrer")
|
||||
case "MediaType":
|
||||
|
||||
out.Values[i] = ec._Referrer_MediaType(ctx, field, obj)
|
||||
|
||||
case "ArtifactType":
|
||||
|
||||
out.Values[i] = ec._Referrer_ArtifactType(ctx, field, obj)
|
||||
|
||||
case "Size":
|
||||
|
||||
out.Values[i] = ec._Referrer_Size(ctx, field, obj)
|
||||
|
||||
case "Digest":
|
||||
|
||||
out.Values[i] = ec._Referrer_Digest(ctx, field, obj)
|
||||
|
||||
case "Annotations":
|
||||
|
||||
out.Values[i] = ec._Referrer_Annotations(ctx, field, obj)
|
||||
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch()
|
||||
if invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var repoInfoImplementors = []string{"RepoInfo"}
|
||||
|
||||
func (ec *executionContext) _RepoInfo(ctx context.Context, sel ast.SelectionSet, obj *RepoInfo) graphql.Marshaler {
|
||||
|
@ -7835,6 +8422,44 @@ func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, o
|
|||
|
||||
// region ***************************** type.gotpl *****************************
|
||||
|
||||
func (ec *executionContext) marshalNAnnotation2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐAnnotation(ctx context.Context, sel ast.SelectionSet, v []*Annotation) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalOAnnotation2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐAnnotation(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) {
|
||||
res, err := graphql.UnmarshalBoolean(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
@ -7888,6 +8513,44 @@ func (ec *executionContext) marshalNImageSummary2ᚖzotregistryᚗioᚋzotᚋpkg
|
|||
return ec._ImageSummary(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNReferrer2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐReferrer(ctx context.Context, sel ast.SelectionSet, v []*Referrer) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalOReferrer2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐReferrer(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNRepoInfo2zotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoInfo(ctx context.Context, sel ast.SelectionSet, v RepoInfo) graphql.Marshaler {
|
||||
return ec._RepoInfo(ctx, sel, &v)
|
||||
}
|
||||
|
@ -8224,6 +8887,13 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a
|
|||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOAnnotation2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐAnnotation(ctx context.Context, sel ast.SelectionSet, v *Annotation) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._Annotation(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) {
|
||||
res, err := graphql.UnmarshalBoolean(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
@ -8615,6 +9285,13 @@ func (ec *executionContext) marshalOPackageInfo2ᚖzotregistryᚗioᚋzotᚋpkg
|
|||
return ec._PackageInfo(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOReferrer2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐReferrer(ctx context.Context, sel ast.SelectionSet, v *Referrer) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._Referrer(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalORepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx context.Context, sel ast.SelectionSet, v []*RepoSummary) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
|
|
@ -6,6 +6,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type Annotation struct {
|
||||
Key *string `json:"Key"`
|
||||
Value *string `json:"Value"`
|
||||
}
|
||||
|
||||
// Contains various details about the CVE and a list of PackageInfo about the affected packages
|
||||
type Cve struct {
|
||||
ID *string `json:"Id"`
|
||||
|
@ -95,6 +100,14 @@ type PackageInfo struct {
|
|||
FixedVersion *string `json:"FixedVersion"`
|
||||
}
|
||||
|
||||
type Referrer struct {
|
||||
MediaType *string `json:"MediaType"`
|
||||
ArtifactType *string `json:"ArtifactType"`
|
||||
Size *int `json:"Size"`
|
||||
Digest *string `json:"Digest"`
|
||||
Annotations []*Annotation `json:"Annotations"`
|
||||
}
|
||||
|
||||
// Contains details about the repo: a list of image summaries and a summary of the repo
|
||||
type RepoInfo struct {
|
||||
Images []*ImageSummary `json:"Images"`
|
||||
|
|
|
@ -576,6 +576,47 @@ func (r *queryResolver) getImageList(store storage.ImageStore, imageName string)
|
|||
return results, nil
|
||||
}
|
||||
|
||||
func getReferrers(store storage.ImageStore, repoName string, digest string, artifactType string, log log.Logger) (
|
||||
[]*gql_generated.Referrer, error,
|
||||
) {
|
||||
results := make([]*gql_generated.Referrer, 0)
|
||||
|
||||
index, err := store.GetReferrers(repoName, godigest.Digest(digest), artifactType)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error extracting referrers list")
|
||||
|
||||
return results, err
|
||||
}
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
size := int(manifest.Size)
|
||||
digest := manifest.Digest.String()
|
||||
annotations := make([]*gql_generated.Annotation, 0)
|
||||
artifactType := manifest.ArtifactType
|
||||
mediaType := manifest.MediaType
|
||||
|
||||
for k, v := range manifest.Annotations {
|
||||
key := k
|
||||
value := v
|
||||
|
||||
annotations = append(annotations, &gql_generated.Annotation{
|
||||
Key: &key,
|
||||
Value: &value,
|
||||
})
|
||||
}
|
||||
|
||||
results = append(results, &gql_generated.Referrer{
|
||||
MediaType: &mediaType,
|
||||
ArtifactType: &artifactType,
|
||||
Digest: &digest,
|
||||
Size: &size,
|
||||
Annotations: annotations,
|
||||
})
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func BuildImageInfo(repo string, tag string, manifestDigest godigest.Digest,
|
||||
manifest ispec.Manifest, imageConfig ispec.Image, isSigned bool,
|
||||
) *gql_generated.ImageSummary {
|
||||
|
|
|
@ -315,6 +315,52 @@ func TestMatching(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGetReferrers(t *testing.T) {
|
||||
Convey("getReferrers", t, func() {
|
||||
Convey("GetReferrers returns error", func() {
|
||||
testLogger := log.NewLogger("debug", "")
|
||||
mockedStore := mocks.MockedImageStore{
|
||||
GetReferrersFn: func(repo string, digest godigest.Digest, artifactType string) (ispec.Index, error) {
|
||||
return ispec.Index{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
_, err := getReferrers(mockedStore, "test", "", "", testLogger)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("GetReferrers return index of descriptors", func() {
|
||||
testLogger := log.NewLogger("debug", "")
|
||||
referrerDescriptor := ispec.Descriptor{
|
||||
MediaType: ispec.MediaTypeArtifactManifest,
|
||||
ArtifactType: "com.artifact.test",
|
||||
Size: 403,
|
||||
Digest: godigest.FromString("test"),
|
||||
Annotations: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
}
|
||||
mockedStore := mocks.MockedImageStore{
|
||||
GetReferrersFn: func(repo string, digest godigest.Digest, artifactType string) (ispec.Index, error) {
|
||||
return ispec.Index{
|
||||
Manifests: []ispec.Descriptor{
|
||||
referrerDescriptor,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
referrers, err := getReferrers(mockedStore, "test", "", "", testLogger)
|
||||
So(err, ShouldBeNil)
|
||||
So(*referrers[0].ArtifactType, ShouldEqual, referrerDescriptor.ArtifactType)
|
||||
So(*referrers[0].MediaType, ShouldEqual, referrerDescriptor.MediaType)
|
||||
So(*referrers[0].Size, ShouldEqual, referrerDescriptor.Size)
|
||||
So(*referrers[0].Digest, ShouldEqual, referrerDescriptor.Digest)
|
||||
So(*referrers[0].Annotations[0].Value, ShouldEqual, referrerDescriptor.Annotations["key"])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestExtractImageDetails(t *testing.T) {
|
||||
Convey("repoListWithNewestImage", t, func() {
|
||||
// log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
|
|
|
@ -134,6 +134,19 @@ type LayerHistory {
|
|||
HistoryDescription: HistoryDescription
|
||||
}
|
||||
|
||||
type Annotation {
|
||||
Key: String
|
||||
Value: String
|
||||
}
|
||||
|
||||
type Referrer {
|
||||
MediaType: String
|
||||
ArtifactType: String
|
||||
Size: Int
|
||||
Digest: String
|
||||
Annotations: [Annotation]!
|
||||
}
|
||||
|
||||
"""
|
||||
Contains details about the supported OS and architecture of the image
|
||||
"""
|
||||
|
@ -197,4 +210,10 @@ type Query {
|
|||
Search for a specific image using its name
|
||||
"""
|
||||
Image(image: String!): ImageSummary
|
||||
|
||||
"""
|
||||
Returns a list of descriptors of an image or artifact manifest that are found in a <repo> and have a subject field of <digest>
|
||||
Can be filtered based on a specific artifact type <type>
|
||||
"""
|
||||
Referrers(repo: String!, digest: String!, type: String!): [Referrer]!
|
||||
}
|
||||
|
|
|
@ -564,6 +564,20 @@ func (r *queryResolver) Image(ctx context.Context, image string) (*gql_generated
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// Referrers is the resolver for the Referrers field.
|
||||
func (r *queryResolver) Referrers(ctx context.Context, repo string, digest string, typeArg string) ([]*gql_generated.Referrer, error) {
|
||||
store := r.storeController.GetImageStore(repo)
|
||||
|
||||
referrers, err := getReferrers(store, repo, digest, typeArg, r.log)
|
||||
if err != nil {
|
||||
r.log.Error().Err(err).Msg("unable to get referrers from default store")
|
||||
|
||||
return []*gql_generated.Referrer{}, err
|
||||
}
|
||||
|
||||
return referrers, nil
|
||||
}
|
||||
|
||||
// Query returns gql_generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() gql_generated.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
|
|
|
@ -446,6 +446,55 @@ func UploadImage(img Image, baseURL, repo string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func UploadArtifact(baseURL, repo string, artifactManifest *imagespec.Artifact) error {
|
||||
// put manifest
|
||||
artifactManifestBlob, err := json.Marshal(artifactManifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
artifactManifestDigest := godigest.FromBytes(artifactManifestBlob)
|
||||
|
||||
_, err = resty.R().
|
||||
SetHeader("Content-type", imagespec.MediaTypeArtifactManifest).
|
||||
SetBody(artifactManifestBlob).
|
||||
Put(baseURL + "/v2/" + repo + "/manifests/" + artifactManifestDigest.String())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func UploadBlob(baseURL, repo string, blob []byte, artifactBlobMediaType string) error {
|
||||
resp, err := resty.R().Post(baseURL + "/v2/" + repo + "/blobs/uploads/")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode() != http.StatusAccepted {
|
||||
return ErrPostBlob
|
||||
}
|
||||
|
||||
loc := resp.Header().Get("Location")
|
||||
|
||||
blobDigest := godigest.FromBytes(blob).String()
|
||||
|
||||
resp, err = resty.R().
|
||||
SetHeader("Content-Length", fmt.Sprintf("%d", len(blob))).
|
||||
SetHeader("Content-Type", artifactBlobMediaType).
|
||||
SetQueryParam("digest", blobDigest).
|
||||
SetBody(blob).
|
||||
Put(baseURL + loc)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode() != http.StatusCreated {
|
||||
return ErrPutBlob
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadLogFileAndSearchString(logPath string, stringToMatch string, timeout time.Duration) (bool, error) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancelFunc()
|
||||
|
|
|
@ -156,6 +156,142 @@ func TestWaitTillTrivyDBDownloadStarted(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestUploadArtifact(t *testing.T) {
|
||||
Convey("Put request results in an error", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
artifact := ispec.Artifact{}
|
||||
|
||||
err := test.UploadArtifact(baseURL, "test", &artifact)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUploadBlob(t *testing.T) {
|
||||
Convey("Post request results in an error", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
err := test.UploadBlob(baseURL, "test", []byte("test"), "zot.com.test")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Post request status differs from accepted", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = tempDir
|
||||
|
||||
err := os.Chmod(tempDir, 0o400)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
err = test.UploadBlob(baseURL, "test", []byte("test"), "zot.com.test")
|
||||
So(err, ShouldEqual, test.ErrPostBlob)
|
||||
})
|
||||
|
||||
Convey("Put request results in an error", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = tempDir
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
blob := new([]byte)
|
||||
|
||||
err := test.UploadBlob(baseURL, "test", *blob, "zot.com.test")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Put request status differs from accepted", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = tempDir
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
blob := []byte("test")
|
||||
blobDigest := godigest.FromBytes(blob)
|
||||
layerPath := path.Join(tempDir, "test", "blobs", "sha256")
|
||||
blobPath := path.Join(layerPath, blobDigest.String())
|
||||
if _, err := os.Stat(layerPath); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(layerPath, 0o700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
file, err := os.Create(blobPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Chmod(layerPath, 0o000)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
err = os.Chmod(layerPath, 0o700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
os.RemoveAll(file.Name())
|
||||
}()
|
||||
}
|
||||
|
||||
err := test.UploadBlob(baseURL, "test", blob, "zot.com.test")
|
||||
So(err, ShouldEqual, test.ErrPutBlob)
|
||||
})
|
||||
|
||||
Convey("Put request successful", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = tempDir
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
blob := []byte("test")
|
||||
|
||||
err := test.UploadBlob(baseURL, "test", blob, "zot.com.test")
|
||||
So(err, ShouldEqual, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUploadImage(t *testing.T) {
|
||||
Convey("Post request results in an error", t, func() {
|
||||
port := test.GetFreePort()
|
||||
|
|
Loading…
Reference in a new issue