mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
Implement RepoListWithNewestImage to return [RepoSummary]
Removed access by index in repoListWithNewestImage Signed-off-by: Catalin Hofnar <catalin.hofnar@gmail.com>
This commit is contained in:
parent
981ca6ddb4
commit
9ca5fa1029
5 changed files with 361 additions and 132 deletions
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"gopkg.in/resty.v1"
|
"gopkg.in/resty.v1"
|
||||||
|
zerr "zotregistry.io/zot/errors"
|
||||||
"zotregistry.io/zot/pkg/api"
|
"zotregistry.io/zot/pkg/api"
|
||||||
"zotregistry.io/zot/pkg/api/config"
|
"zotregistry.io/zot/pkg/api/config"
|
||||||
"zotregistry.io/zot/pkg/api/constants"
|
"zotregistry.io/zot/pkg/api/constants"
|
||||||
|
@ -51,9 +52,9 @@ var (
|
||||||
subRootDir string
|
subRootDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
type ImgResponsWithLatestTag struct {
|
type RepoWithNewestImageResponse struct {
|
||||||
ImgListWithLatestTag ImgListWithLatestTag `json:"data"`
|
RepoListWithNewestImage RepoListWithNewestImage `json:"data"`
|
||||||
Errors []ErrorGQL `json:"errors"`
|
Errors []ErrorGQL `json:"errors"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpandedRepoInfoResp struct {
|
type ExpandedRepoInfoResp struct {
|
||||||
|
@ -112,8 +113,8 @@ type ExpandedRepoInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:tagliatelle // graphQL schema
|
//nolint:tagliatelle // graphQL schema
|
||||||
type ImgListWithLatestTag struct {
|
type RepoListWithNewestImage struct {
|
||||||
Images []ImageInfo `json:"ImageListWithLatestTag"`
|
Repos []RepoSummary `json:"RepoListWithNewestImage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorGQL struct {
|
type ErrorGQL struct {
|
||||||
|
@ -318,8 +319,132 @@ func TestImageFormat(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLatestTagSearchHTTP(t *testing.T) {
|
func TestRepoListWithNewestImage(t *testing.T) {
|
||||||
Convey("Test latest image search by timestamp", t, func() {
|
Convey("Test repoListWithNewestImage AddError", t, func() {
|
||||||
|
subpath := "/a"
|
||||||
|
err := testSetup(t, subpath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(path.Join(rootDir, "zot-cve-test"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(path.Join(rootDir, subpath))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
port := GetFreePort()
|
||||||
|
baseURL := GetBaseURL(port)
|
||||||
|
conf := config.New()
|
||||||
|
conf.HTTP.Port = port
|
||||||
|
conf.Storage.RootDirectory = rootDir
|
||||||
|
defaultVal := true
|
||||||
|
conf.Extensions = &extconf.ExtensionConfig{
|
||||||
|
Search: &extconf.SearchConfig{Enable: &defaultVal},
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Extensions.Search.CVE = nil
|
||||||
|
|
||||||
|
ctlr := api.NewController(conf)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// this blocks
|
||||||
|
if err := ctlr.Run(context.Background()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait till ready
|
||||||
|
for {
|
||||||
|
_, err := resty.R().Get(baseURL)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// shut down server
|
||||||
|
defer func() {
|
||||||
|
ctx := context.Background()
|
||||||
|
_ = ctlr.Server.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
resp, err := resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
err = os.Remove(path.Join(rootDir,
|
||||||
|
"zot-test/blobs/sha256/2bacca16b9df395fc855c14ccf50b12b58d35d468b8e7f25758aff90f89bf396"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
errmsg := fmt.Sprint(zerr.ErrBlobNotFound)
|
||||||
|
body := string(resp.Body())
|
||||||
|
So(body, ShouldContainSubstring, errmsg)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
err = CopyFiles("../../../../test/data/zot-test", path.Join(rootDir, "zot-test"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(path.Join(rootDir,
|
||||||
|
"zot-test/blobs/sha256/adf3bb6cc81f8bd6a9d5233be5f0c1a4f1e3ed1cf5bbdfad7708cc8d4099b741"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(path.Join(rootDir,
|
||||||
|
"zot-test/blobs/sha256/2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
errmsg = fmt.Sprint(zerr.ErrBlobNotFound)
|
||||||
|
body = string(resp.Body())
|
||||||
|
So(body, ShouldContainSubstring, errmsg)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
err = CopyFiles("../../../../test/data/zot-test", path.Join(rootDir, "zot-test"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(path.Join(rootDir, "zot-test/index.json"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
//nolint: lll
|
||||||
|
manifestNoAnnotations := "{\"schemaVersion\":2,\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:2bacca16b9df395fc855c14ccf50b12b58d35d468b8e7f25758aff90f89bf396\",\"size\":350}]}"
|
||||||
|
err = os.WriteFile(path.Join(rootDir, "zot-test/index.json"), []byte(manifestNoAnnotations), 0o600)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
body = string(resp.Body())
|
||||||
|
So(body, ShouldContainSubstring, "reference not found for this manifest")
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Test repoListWithNewestImage by tag with HTTP", t, func() {
|
||||||
subpath := "/a"
|
subpath := "/a"
|
||||||
err := testSetup(t, subpath)
|
err := testSetup(t, subpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -373,20 +498,22 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 422)
|
So(resp.StatusCode(), ShouldEqual, 422)
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
var responseStruct ImgResponsWithLatestTag
|
var responseStruct RepoWithNewestImageResponse
|
||||||
err = json.Unmarshal(resp.Body(), &responseStruct)
|
err = json.Unmarshal(resp.Body(), &responseStruct)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(responseStruct.ImgListWithLatestTag.Images), ShouldEqual, 4)
|
So(len(responseStruct.RepoListWithNewestImage.Repos), ShouldEqual, 4)
|
||||||
|
|
||||||
images := responseStruct.ImgListWithLatestTag.Images
|
images := responseStruct.RepoListWithNewestImage.Repos
|
||||||
So(images[0].Tag, ShouldEqual, "0.0.1")
|
So(images[0].NewestImage.Tag, ShouldEqual, "0.0.1")
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
@ -395,14 +522,15 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
err = json.Unmarshal(resp.Body(), &responseStruct)
|
err = json.Unmarshal(resp.Body(), &responseStruct)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(responseStruct.ImgListWithLatestTag.Images), ShouldEqual, 0)
|
So(responseStruct.Errors, ShouldNotBeNil)
|
||||||
|
|
||||||
err = os.Chmod(rootDir, 0o755)
|
err = os.Chmod(rootDir, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -419,7 +547,8 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
@ -430,7 +559,8 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
@ -440,7 +570,8 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
@ -451,7 +582,8 @@ func TestLatestTagSearchHTTP(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query={ImageListWithLatestTag(){RepoName%20Tag}}")
|
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix +
|
||||||
|
"?query={RepoListWithNewestImage{Name%20NewestImage{Tag}}}")
|
||||||
So(resp, ShouldNotBeNil)
|
So(resp, ShouldNotBeNil)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 200)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
|
@ -98,14 +98,14 @@ type ComplexityRoot struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
Query struct {
|
Query struct {
|
||||||
CVEListForImage func(childComplexity int, image string) int
|
CVEListForImage func(childComplexity int, image string) int
|
||||||
ExpandedRepoInfo func(childComplexity int, repo string) int
|
ExpandedRepoInfo func(childComplexity int, repo string) int
|
||||||
GlobalSearch func(childComplexity int, query string) int
|
GlobalSearch func(childComplexity int, query string) int
|
||||||
ImageList func(childComplexity int, repo string) int
|
ImageList func(childComplexity int, repo string) int
|
||||||
ImageListForCve func(childComplexity int, id string) int
|
ImageListForCve func(childComplexity int, id string) int
|
||||||
ImageListForDigest func(childComplexity int, id string) int
|
ImageListForDigest func(childComplexity int, id string) int
|
||||||
ImageListWithCVEFixed func(childComplexity int, id string, image string) int
|
ImageListWithCVEFixed func(childComplexity int, id string, image string) int
|
||||||
ImageListWithLatestTag func(childComplexity int) int
|
RepoListWithNewestImage func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
RepoInfo struct {
|
RepoInfo struct {
|
||||||
|
@ -132,7 +132,7 @@ type QueryResolver interface {
|
||||||
ImageListForCve(ctx context.Context, id string) ([]*ImageSummary, error)
|
ImageListForCve(ctx context.Context, id string) ([]*ImageSummary, error)
|
||||||
ImageListWithCVEFixed(ctx context.Context, id string, image string) ([]*ImageSummary, error)
|
ImageListWithCVEFixed(ctx context.Context, id string, image string) ([]*ImageSummary, error)
|
||||||
ImageListForDigest(ctx context.Context, id string) ([]*ImageSummary, error)
|
ImageListForDigest(ctx context.Context, id string) ([]*ImageSummary, error)
|
||||||
ImageListWithLatestTag(ctx context.Context) ([]*ImageSummary, error)
|
RepoListWithNewestImage(ctx context.Context) ([]*RepoSummary, error)
|
||||||
ImageList(ctx context.Context, repo string) ([]*ImageSummary, error)
|
ImageList(ctx context.Context, repo string) ([]*ImageSummary, error)
|
||||||
ExpandedRepoInfo(ctx context.Context, repo string) (*RepoInfo, error)
|
ExpandedRepoInfo(ctx context.Context, repo string) (*RepoInfo, error)
|
||||||
GlobalSearch(ctx context.Context, query string) (*GlobalSearchResult, error)
|
GlobalSearch(ctx context.Context, query string) (*GlobalSearchResult, error)
|
||||||
|
@ -468,12 +468,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Query.ImageListWithCVEFixed(childComplexity, args["id"].(string), args["image"].(string)), true
|
return e.complexity.Query.ImageListWithCVEFixed(childComplexity, args["id"].(string), args["image"].(string)), true
|
||||||
|
|
||||||
case "Query.ImageListWithLatestTag":
|
case "Query.RepoListWithNewestImage":
|
||||||
if e.complexity.Query.ImageListWithLatestTag == nil {
|
if e.complexity.Query.RepoListWithNewestImage == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.ImageListWithLatestTag(childComplexity), true
|
return e.complexity.Query.RepoListWithNewestImage(childComplexity), true
|
||||||
|
|
||||||
case "RepoInfo.Images":
|
case "RepoInfo.Images":
|
||||||
if e.complexity.RepoInfo.Images == nil {
|
if e.complexity.RepoInfo.Images == nil {
|
||||||
|
@ -697,7 +697,7 @@ type Query {
|
||||||
ImageListForCVE(id: String!): [ImageSummary!]
|
ImageListForCVE(id: String!): [ImageSummary!]
|
||||||
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
|
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
|
||||||
ImageListForDigest(id: String!): [ImageSummary!]
|
ImageListForDigest(id: String!): [ImageSummary!]
|
||||||
ImageListWithLatestTag: [ImageSummary!]
|
RepoListWithNewestImage: [RepoSummary!]! # Newest based on created timestamp
|
||||||
ImageList(repo: String!): [ImageSummary!]
|
ImageList(repo: String!): [ImageSummary!]
|
||||||
ExpandedRepoInfo(repo: String!): RepoInfo!
|
ExpandedRepoInfo(repo: String!): RepoInfo!
|
||||||
GlobalSearch(query: String!): GlobalSearchResult!
|
GlobalSearch(query: String!): GlobalSearchResult!
|
||||||
|
@ -2639,8 +2639,8 @@ func (ec *executionContext) fieldContext_Query_ImageListForDigest(ctx context.Co
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Query_ImageListWithLatestTag(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Query_RepoListWithNewestImage(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Query_ImageListWithLatestTag(ctx, field)
|
fc, err := ec.fieldContext_Query_RepoListWithNewestImage(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
|
@ -2653,21 +2653,24 @@ func (ec *executionContext) _Query_ImageListWithLatestTag(ctx context.Context, f
|
||||||
}()
|
}()
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().ImageListWithLatestTag(rctx)
|
return ec.resolvers.Query().RepoListWithNewestImage(rctx)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
if resTmp == nil {
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
res := resTmp.([]*ImageSummary)
|
res := resTmp.([]*RepoSummary)
|
||||||
fc.Result = res
|
fc.Result = res
|
||||||
return ec.marshalOImageSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummaryᚄ(ctx, field.Selections, res)
|
return ec.marshalNRepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummaryᚄ(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) fieldContext_Query_ImageListWithLatestTag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
func (ec *executionContext) fieldContext_Query_RepoListWithNewestImage(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Query",
|
Object: "Query",
|
||||||
Field: field,
|
Field: field,
|
||||||
|
@ -2675,38 +2678,28 @@ func (ec *executionContext) fieldContext_Query_ImageListWithLatestTag(ctx contex
|
||||||
IsResolver: true,
|
IsResolver: true,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "RepoName":
|
case "Name":
|
||||||
return ec.fieldContext_ImageSummary_RepoName(ctx, field)
|
return ec.fieldContext_RepoSummary_Name(ctx, field)
|
||||||
case "Tag":
|
|
||||||
return ec.fieldContext_ImageSummary_Tag(ctx, field)
|
|
||||||
case "Digest":
|
|
||||||
return ec.fieldContext_ImageSummary_Digest(ctx, field)
|
|
||||||
case "ConfigDigest":
|
|
||||||
return ec.fieldContext_ImageSummary_ConfigDigest(ctx, field)
|
|
||||||
case "LastUpdated":
|
case "LastUpdated":
|
||||||
return ec.fieldContext_ImageSummary_LastUpdated(ctx, field)
|
return ec.fieldContext_RepoSummary_LastUpdated(ctx, field)
|
||||||
case "IsSigned":
|
|
||||||
return ec.fieldContext_ImageSummary_IsSigned(ctx, field)
|
|
||||||
case "Size":
|
case "Size":
|
||||||
return ec.fieldContext_ImageSummary_Size(ctx, field)
|
return ec.fieldContext_RepoSummary_Size(ctx, field)
|
||||||
case "Platform":
|
case "Platforms":
|
||||||
return ec.fieldContext_ImageSummary_Platform(ctx, field)
|
return ec.fieldContext_RepoSummary_Platforms(ctx, field)
|
||||||
case "Vendor":
|
case "Vendors":
|
||||||
return ec.fieldContext_ImageSummary_Vendor(ctx, field)
|
return ec.fieldContext_RepoSummary_Vendors(ctx, field)
|
||||||
case "Score":
|
case "Score":
|
||||||
return ec.fieldContext_ImageSummary_Score(ctx, field)
|
return ec.fieldContext_RepoSummary_Score(ctx, field)
|
||||||
|
case "NewestImage":
|
||||||
|
return ec.fieldContext_RepoSummary_NewestImage(ctx, field)
|
||||||
case "DownloadCount":
|
case "DownloadCount":
|
||||||
return ec.fieldContext_ImageSummary_DownloadCount(ctx, field)
|
return ec.fieldContext_RepoSummary_DownloadCount(ctx, field)
|
||||||
case "Layers":
|
case "StarCount":
|
||||||
return ec.fieldContext_ImageSummary_Layers(ctx, field)
|
return ec.fieldContext_RepoSummary_StarCount(ctx, field)
|
||||||
case "Description":
|
case "IsBookmarked":
|
||||||
return ec.fieldContext_ImageSummary_Description(ctx, field)
|
return ec.fieldContext_RepoSummary_IsBookmarked(ctx, field)
|
||||||
case "Licenses":
|
|
||||||
return ec.fieldContext_ImageSummary_Licenses(ctx, field)
|
|
||||||
case "Labels":
|
|
||||||
return ec.fieldContext_ImageSummary_Labels(ctx, field)
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no field named %q was found under type ImageSummary", field.Name)
|
return nil, fmt.Errorf("no field named %q was found under type RepoSummary", field.Name)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return fc, nil
|
return fc, nil
|
||||||
|
@ -5795,7 +5788,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||||
out.Concurrently(i, func() graphql.Marshaler {
|
out.Concurrently(i, func() graphql.Marshaler {
|
||||||
return rrm(innerCtx)
|
return rrm(innerCtx)
|
||||||
})
|
})
|
||||||
case "ImageListWithLatestTag":
|
case "RepoListWithNewestImage":
|
||||||
field := field
|
field := field
|
||||||
|
|
||||||
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
|
innerFunc := func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
|
@ -5804,7 +5797,10 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
res = ec._Query_ImageListWithLatestTag(ctx, field)
|
res = ec._Query_RepoListWithNewestImage(ctx, field)
|
||||||
|
if res == graphql.Null {
|
||||||
|
atomic.AddUint32(&invalids, 1)
|
||||||
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6379,6 +6375,60 @@ func (ec *executionContext) marshalNRepoInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋe
|
||||||
return ec._RepoInfo(ctx, sel, v)
|
return ec._RepoInfo(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNRepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummaryᚄ(ctx context.Context, sel ast.SelectionSet, v []*RepoSummary) 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.marshalNRepoSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx, sel, v[i])
|
||||||
|
}
|
||||||
|
if isLen1 {
|
||||||
|
f(i)
|
||||||
|
} else {
|
||||||
|
go f(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
for _, e := range ret {
|
||||||
|
if e == graphql.Null {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNRepoSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx context.Context, sel ast.SelectionSet, v *RepoSummary) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec._RepoSummary(ctx, sel, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) {
|
func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) {
|
||||||
res, err := graphql.UnmarshalString(v)
|
res, err := graphql.UnmarshalString(v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
|
|
@ -11,10 +11,12 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
glob "github.com/bmatcuk/doublestar/v4"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
glob "github.com/bmatcuk/doublestar/v4" // nolint:gci
|
||||||
|
v1 "github.com/google/go-containerregistry/pkg/v1" // nolint:gci
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
"zotregistry.io/zot/pkg/extensions/search/common"
|
"zotregistry.io/zot/pkg/extensions/search/common"
|
||||||
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
|
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
|
||||||
digestinfo "zotregistry.io/zot/pkg/extensions/search/digest"
|
digestinfo "zotregistry.io/zot/pkg/extensions/search/digest"
|
||||||
|
@ -114,80 +116,125 @@ func (r *queryResolver) getImageListForDigest(repoList []string, digest string)
|
||||||
return imgResultForDigest, errResult
|
return imgResultForDigest, errResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*gql_generated.ImageSummary, error) {
|
// nolint:lll
|
||||||
results := make([]*gql_generated.ImageSummary, 0)
|
func (r *queryResolver) repoListWithNewestImage(ctx context.Context, store storage.ImageStore) ([]*gql_generated.RepoSummary, error) {
|
||||||
|
repos := []*gql_generated.RepoSummary{}
|
||||||
|
olu := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
||||||
|
|
||||||
repoList, err := store.GetRepositories()
|
repoNames, err := store.GetRepositories()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Error().Err(err).Msg("extension api: error extracting repositories list")
|
return nil, err
|
||||||
|
|
||||||
return results, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(repoList) == 0 {
|
for _, repo := range repoNames {
|
||||||
r.log.Info().Msg("no repositories found")
|
lastUpdatedTag, err := olu.GetRepoLastUpdated(repo)
|
||||||
}
|
|
||||||
|
|
||||||
layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
|
||||||
|
|
||||||
for _, repo := range repoList {
|
|
||||||
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Error().Err(err).Msg("extension api: error getting tag timestamp info")
|
graphql.AddError(ctx, err)
|
||||||
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tagsInfo) == 0 {
|
|
||||||
r.log.Info().Str("no tagsinfo found for repo", repo).Msg(" continuing traversing")
|
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
latestTag := common.GetLatestTag(tagsInfo)
|
repoSize := int64(0)
|
||||||
|
repoBlob2Size := make(map[string]int64, 10)
|
||||||
|
tagsInfo, _ := olu.GetImageTagsWithTimestamp(repo)
|
||||||
|
|
||||||
digest := godigest.Digest(latestTag.Digest)
|
manifests, err := olu.GetImageManifests(repo)
|
||||||
|
|
||||||
manifest, err := layoutUtils.GetImageBlobManifest(repo, digest)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Error().Err(err).Msg("extension api: error reading manifest")
|
graphql.AddError(ctx, err)
|
||||||
|
|
||||||
return results, err
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
size := strconv.FormatInt(manifest.Config.Size, 10)
|
repoPlatforms := make([]*gql_generated.OsArch, 0, len(tagsInfo))
|
||||||
|
repoVendors := make([]*string, 0, len(manifests))
|
||||||
|
repoName := repo
|
||||||
|
|
||||||
name := repo
|
var lastUpdatedImageSummary gql_generated.ImageSummary
|
||||||
|
|
||||||
imageConfig, err := layoutUtils.GetImageInfo(repo, manifest.Config.Digest)
|
var brokenManifest bool
|
||||||
if err != nil {
|
|
||||||
r.log.Error().Err(err).Msg("extension api: error reading image config")
|
|
||||||
|
|
||||||
return results, err
|
for i, manifest := range manifests {
|
||||||
|
imageLayersSize := int64(0)
|
||||||
|
manifestSize := olu.GetImageManifestSize(repo, manifests[i].Digest)
|
||||||
|
|
||||||
|
imageBlobManifest, _ := olu.GetImageBlobManifest(repo, manifests[i].Digest)
|
||||||
|
|
||||||
|
configSize := imageBlobManifest.Config.Size
|
||||||
|
repoBlob2Size[manifests[i].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, _ := olu.GetImageConfigInfo(repo, manifests[i].Digest)
|
||||||
|
|
||||||
|
os, arch := olu.GetImagePlatform(imageConfigInfo)
|
||||||
|
osArch := &gql_generated.OsArch{
|
||||||
|
Os: &os,
|
||||||
|
Arch: &arch,
|
||||||
|
}
|
||||||
|
repoPlatforms = append(repoPlatforms, osArch)
|
||||||
|
|
||||||
|
vendor := olu.GetImageVendor(imageConfigInfo)
|
||||||
|
repoVendors = append(repoVendors, &vendor)
|
||||||
|
|
||||||
|
manifestTag, ok := manifest.Annotations[ispec.AnnotationRefName]
|
||||||
|
if !ok {
|
||||||
|
graphql.AddError(ctx, gqlerror.Errorf("reference not found for this manifest"))
|
||||||
|
brokenManifest = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := manifestTag
|
||||||
|
size := strconv.Itoa(int(imageSize))
|
||||||
|
isSigned := olu.CheckManifestSignature(repo, manifests[i].Digest)
|
||||||
|
lastUpdated := olu.GetImageLastUpdated(imageConfigInfo)
|
||||||
|
score := 0
|
||||||
|
|
||||||
|
imageSummary := gql_generated.ImageSummary{
|
||||||
|
RepoName: &repoName,
|
||||||
|
Tag: &tag,
|
||||||
|
LastUpdated: &lastUpdated,
|
||||||
|
IsSigned: &isSigned,
|
||||||
|
Size: &size,
|
||||||
|
Platform: osArch,
|
||||||
|
Vendor: &vendor,
|
||||||
|
Score: &score,
|
||||||
|
}
|
||||||
|
|
||||||
|
if tagsInfo[i].Digest == lastUpdatedTag.Digest {
|
||||||
|
lastUpdatedImageSummary = imageSummary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := imageConfig.Config.Labels
|
if brokenManifest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Read Description
|
for blob := range repoBlob2Size {
|
||||||
desc := common.GetDescription(labels)
|
repoSize += repoBlob2Size[blob]
|
||||||
|
}
|
||||||
|
|
||||||
// Read licenses
|
repoSizeStr := strconv.FormatInt(repoSize, 10)
|
||||||
license := common.GetLicense(labels)
|
index := 0
|
||||||
|
|
||||||
// Read vendor
|
repos = append(repos, &gql_generated.RepoSummary{
|
||||||
vendor := common.GetVendor(labels)
|
Name: &repoName,
|
||||||
|
LastUpdated: &lastUpdatedTag.Timestamp,
|
||||||
// Read categories
|
Size: &repoSizeStr,
|
||||||
categories := common.GetCategories(labels)
|
Platforms: repoPlatforms,
|
||||||
|
Vendors: repoVendors,
|
||||||
results = append(results, &gql_generated.ImageSummary{
|
Score: &index,
|
||||||
RepoName: &name, Tag: &latestTag.Name,
|
NewestImage: &lastUpdatedImageSummary,
|
||||||
Description: &desc, Licenses: &license, Vendor: &vendor,
|
|
||||||
Labels: &categories, Size: &size, LastUpdated: &latestTag.Timestamp,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return repos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanQuerry(query string) string {
|
func cleanQuerry(query string) string {
|
||||||
|
|
|
@ -84,7 +84,7 @@ type Query {
|
||||||
ImageListForCVE(id: String!): [ImageSummary!]
|
ImageListForCVE(id: String!): [ImageSummary!]
|
||||||
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
|
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
|
||||||
ImageListForDigest(id: String!): [ImageSummary!]
|
ImageListForDigest(id: String!): [ImageSummary!]
|
||||||
ImageListWithLatestTag: [ImageSummary!]
|
RepoListWithNewestImage: [RepoSummary!]! # Newest based on created timestamp
|
||||||
ImageList(repo: String!): [ImageSummary!]
|
ImageList(repo: String!): [ImageSummary!]
|
||||||
ExpandedRepoInfo(repo: String!): RepoInfo!
|
ExpandedRepoInfo(repo: String!): RepoInfo!
|
||||||
GlobalSearch(query: String!): GlobalSearchResult!
|
GlobalSearch(query: String!): GlobalSearchResult!
|
||||||
|
|
|
@ -287,41 +287,41 @@ func (r *queryResolver) ImageListForDigest(ctx context.Context, id string) ([]*g
|
||||||
return imgResultForDigest, nil
|
return imgResultForDigest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageListWithLatestTag is the resolver for the ImageListWithLatestTag field.
|
// RepoListWithNewestImage is the resolver for the RepoListWithNewestImage field.
|
||||||
func (r *queryResolver) ImageListWithLatestTag(ctx context.Context) ([]*gql_generated.ImageSummary, error) {
|
func (r *queryResolver) RepoListWithNewestImage(ctx context.Context) ([]*gql_generated.RepoSummary, error) {
|
||||||
r.log.Info().Msg("extension api: finding image list")
|
r.log.Info().Msg("extension api: finding image list")
|
||||||
|
|
||||||
imageList := make([]*gql_generated.ImageSummary, 0)
|
repoList := make([]*gql_generated.RepoSummary, 0)
|
||||||
|
|
||||||
defaultStore := r.storeController.DefaultStore
|
defaultStore := r.storeController.DefaultStore
|
||||||
|
|
||||||
dsImageList, err := r.getImageListWithLatestTag(defaultStore)
|
dsRepoList, err := r.repoListWithNewestImage(ctx, defaultStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Error().Err(err).Msg("extension api: error extracting default store image list")
|
r.log.Error().Err(err).Msg("extension api: error extracting default store image list")
|
||||||
|
|
||||||
return imageList, err
|
return repoList, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dsImageList) != 0 {
|
if len(dsRepoList) != 0 {
|
||||||
imageList = append(imageList, dsImageList...)
|
repoList = append(repoList, dsRepoList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
subStore := r.storeController.SubStore
|
subStore := r.storeController.SubStore
|
||||||
|
|
||||||
for _, store := range subStore {
|
for _, store := range subStore {
|
||||||
ssImageList, err := r.getImageListWithLatestTag(store)
|
ssRepoList, err := r.repoListWithNewestImage(ctx, store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.log.Error().Err(err).Msg("extension api: error extracting default store image list")
|
r.log.Error().Err(err).Msg("extension api: error extracting substore image list")
|
||||||
|
|
||||||
return imageList, err
|
return repoList, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ssImageList) != 0 {
|
if len(ssRepoList) != 0 {
|
||||||
imageList = append(imageList, ssImageList...)
|
repoList = append(repoList, ssRepoList...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageList, nil
|
return repoList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageList is the resolver for the ImageList field.
|
// ImageList is the resolver for the ImageList field.
|
||||||
|
|
Loading…
Reference in a new issue