From 418a1a006ce1b31975032f0b8927ec7625e7e684 Mon Sep 17 00:00:00 2001 From: Andrei Aaron Date: Tue, 11 Jul 2023 19:29:04 +0300 Subject: [PATCH] feat(cve): ability to return CVEs per image os and architecture (#1607) Signed-off-by: Andrei Aaron --- .../search/gql_generated/generated.go | 58 +++-- pkg/extensions/search/resolver.go | 36 ++- pkg/extensions/search/resolver_test.go | 220 ++++++++++++----- pkg/extensions/search/schema.graphql | 4 + pkg/extensions/search/schema.resolvers.go | 12 +- .../repodb/boltdb-wrapper/boltdb_wrapper.go | 26 +- .../boltdb-wrapper/boltdb_wrapper_test.go | 68 ++++++ .../repodb/dynamodb-wrapper/dynamo_test.go | 71 ++++++ .../repodb/dynamodb-wrapper/dynamo_wrapper.go | 30 ++- pkg/meta/repodb/repodb.go | 2 +- pkg/meta/repodb/repodb_test.go | 222 ++++++++++++++++++ pkg/test/mocks/repo_db_mock.go | 6 +- 12 files changed, 658 insertions(+), 97 deletions(-) diff --git a/pkg/extensions/search/gql_generated/generated.go b/pkg/extensions/search/gql_generated/generated.go index c3c41292..fdf40b20 100644 --- a/pkg/extensions/search/gql_generated/generated.go +++ b/pkg/extensions/search/gql_generated/generated.go @@ -166,9 +166,9 @@ type ComplexityRoot struct { GlobalSearch func(childComplexity int, query string, filter *Filter, requestedPage *PageInput) int Image func(childComplexity int, image string) int ImageList func(childComplexity int, repo string, requestedPage *PageInput) int - ImageListForCve func(childComplexity int, id string, requestedPage *PageInput) int + ImageListForCve func(childComplexity int, id string, filter *Filter, requestedPage *PageInput) int ImageListForDigest func(childComplexity int, id string, requestedPage *PageInput) int - ImageListWithCVEFixed func(childComplexity int, id string, image string, requestedPage *PageInput) int + ImageListWithCVEFixed func(childComplexity int, id string, image string, filter *Filter, requestedPage *PageInput) int Referrers func(childComplexity int, repo string, digest string, typeArg []string) int RepoListWithNewestImage func(childComplexity int, requestedPage *PageInput) int StarredRepos func(childComplexity int, requestedPage *PageInput) int @@ -209,8 +209,8 @@ type ComplexityRoot struct { type QueryResolver interface { CVEListForImage(ctx context.Context, image string, requestedPage *PageInput, searchedCve *string) (*CVEResultForImage, error) - ImageListForCve(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error) - ImageListWithCVEFixed(ctx context.Context, id string, image string, requestedPage *PageInput) (*PaginatedImagesResult, error) + ImageListForCve(ctx context.Context, id string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error) + ImageListWithCVEFixed(ctx context.Context, id string, image string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error) ImageListForDigest(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error) RepoListWithNewestImage(ctx context.Context, requestedPage *PageInput) (*PaginatedReposResult, error) ImageList(ctx context.Context, repo string, requestedPage *PageInput) (*PaginatedImagesResult, error) @@ -828,7 +828,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.ImageListForCve(childComplexity, args["id"].(string), args["requestedPage"].(*PageInput)), true + return e.complexity.Query.ImageListForCve(childComplexity, args["id"].(string), args["filter"].(*Filter), args["requestedPage"].(*PageInput)), true case "Query.ImageListForDigest": if e.complexity.Query.ImageListForDigest == nil { @@ -852,7 +852,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.ImageListWithCVEFixed(childComplexity, args["id"].(string), args["image"].(string), args["requestedPage"].(*PageInput)), true + return e.complexity.Query.ImageListWithCVEFixed(childComplexity, args["id"].(string), args["image"].(string), args["filter"].(*Filter), args["requestedPage"].(*PageInput)), true case "Query.Referrers": if e.complexity.Query.Referrers == nil { @@ -1693,6 +1693,8 @@ type Query { ImageListForCVE( "CVE ID" id: String!, + "Filter to apply before returning the results" + filter: Filter, "Sets the parameters of the requested page" requestedPage: PageInput ): PaginatedImagesResult! @@ -1706,6 +1708,8 @@ type Query { id: String!, "Repository name" image: String!, + "Filter to apply before returning the results" + filter: Filter, "Sets the parameters of the requested page" requestedPage: PageInput ): PaginatedImagesResult! @@ -2004,15 +2008,24 @@ func (ec *executionContext) field_Query_ImageListForCVE_args(ctx context.Context } } args["id"] = arg0 - var arg1 *PageInput - if tmp, ok := rawArgs["requestedPage"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("requestedPage")) - arg1, err = ec.unmarshalOPageInput2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInput(ctx, tmp) + var arg1 *Filter + if tmp, ok := rawArgs["filter"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + arg1, err = ec.unmarshalOFilter2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐFilter(ctx, tmp) if err != nil { return nil, err } } - args["requestedPage"] = arg1 + args["filter"] = arg1 + var arg2 *PageInput + if tmp, ok := rawArgs["requestedPage"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("requestedPage")) + arg2, err = ec.unmarshalOPageInput2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["requestedPage"] = arg2 return args, nil } @@ -2061,15 +2074,24 @@ func (ec *executionContext) field_Query_ImageListWithCVEFixed_args(ctx context.C } } args["image"] = arg1 - var arg2 *PageInput - if tmp, ok := rawArgs["requestedPage"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("requestedPage")) - arg2, err = ec.unmarshalOPageInput2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInput(ctx, tmp) + var arg2 *Filter + if tmp, ok := rawArgs["filter"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + arg2, err = ec.unmarshalOFilter2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐFilter(ctx, tmp) if err != nil { return nil, err } } - args["requestedPage"] = arg2 + args["filter"] = arg2 + var arg3 *PageInput + if tmp, ok := rawArgs["requestedPage"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("requestedPage")) + arg3, err = ec.unmarshalOPageInput2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPageInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["requestedPage"] = arg3 return args, nil } @@ -5440,7 +5462,7 @@ func (ec *executionContext) _Query_ImageListForCVE(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().ImageListForCve(rctx, fc.Args["id"].(string), fc.Args["requestedPage"].(*PageInput)) + return ec.resolvers.Query().ImageListForCve(rctx, fc.Args["id"].(string), fc.Args["filter"].(*Filter), fc.Args["requestedPage"].(*PageInput)) }) if err != nil { ec.Error(ctx, err) @@ -5501,7 +5523,7 @@ func (ec *executionContext) _Query_ImageListWithCVEFixed(ctx context.Context, fi }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().ImageListWithCVEFixed(rctx, fc.Args["id"].(string), fc.Args["image"].(string), fc.Args["requestedPage"].(*PageInput)) + return ec.resolvers.Query().ImageListWithCVEFixed(rctx, fc.Args["id"].(string), fc.Args["image"].(string), fc.Args["filter"].(*Filter), fc.Args["requestedPage"].(*PageInput)) }) if err != nil { ec.Error(ctx, err) diff --git a/pkg/extensions/search/resolver.go b/pkg/extensions/search/resolver.go index e33be12a..d6423acc 100644 --- a/pkg/extensions/search/resolver.go +++ b/pkg/extensions/search/resolver.go @@ -133,7 +133,8 @@ func getImageListForDigest(ctx context.Context, digest string, repoDB repodb.Rep } // get all repos - reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, FilterByDigest(digest), pageInput) + reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, + FilterByDigest(digest), repodb.Filter{}, pageInput) if err != nil { return &gql_generated.PaginatedImagesResult{}, err } @@ -383,6 +384,7 @@ func getImageListForCVE( ctx context.Context, cveID string, cveInfo cveinfo.CveInfo, + filter *gql_generated.Filter, requestedPage *gql_generated.PageInput, repoDB repodb.RepoDB, log log.Logger, @@ -426,6 +428,17 @@ func getImageListForCVE( requestedPage = &gql_generated.PageInput{} } + localFilter := repodb.Filter{} + if filter != nil { + localFilter = repodb.Filter{ + Os: filter.Os, + Arch: filter.Arch, + HasToBeSigned: filter.HasToBeSigned, + IsBookmarked: filter.IsBookmarked, + IsStarred: filter.IsStarred, + } + } + // Actual page requested by user pageInput := repodb.PageInput{ Limit: safeDereferencing(requestedPage.Limit, 0), @@ -437,7 +450,7 @@ func getImageListForCVE( // get all repos reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, - FilterByTagInfo(affectedImages), pageInput) + FilterByTagInfo(affectedImages), localFilter, pageInput) if err != nil { return &gql_generated.PaginatedImagesResult{}, err } @@ -462,6 +475,7 @@ func getImageListWithCVEFixed( cveID string, repo string, cveInfo cveinfo.CveInfo, + filter *gql_generated.Filter, requestedPage *gql_generated.PageInput, repoDB repodb.RepoDB, log log.Logger, @@ -488,6 +502,17 @@ func getImageListWithCVEFixed( requestedPage = &gql_generated.PageInput{} } + localFilter := repodb.Filter{} + if filter != nil { + localFilter = repodb.Filter{ + Os: filter.Os, + Arch: filter.Arch, + HasToBeSigned: filter.HasToBeSigned, + IsBookmarked: filter.IsBookmarked, + IsStarred: filter.IsStarred, + } + } + // Actual page requested by user pageInput := repodb.PageInput{ Limit: safeDereferencing(requestedPage.Limit, 0), @@ -498,7 +523,9 @@ func getImageListWithCVEFixed( } // get all repos - reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, FilterByTagInfo(tagsInfo), pageInput) + reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, + FilterByTagInfo(tagsInfo), localFilter, pageInput, + ) if err != nil { return &gql_generated.PaginatedImagesResult{}, err } @@ -786,6 +813,7 @@ func derivedImageList(ctx context.Context, image string, digest *string, repoDB // we need all available tags reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, filterDerivedImages(searchedImage), + repodb.Filter{}, pageInput) if err != nil { return &gql_generated.PaginatedImagesResult{}, err @@ -900,6 +928,7 @@ func baseImageList(ctx context.Context, image string, digest *string, repoDB rep // we need all available tags reposMeta, manifestMetaMap, indexDataMap, pageInfo, err := repoDB.FilterTags(ctx, filterBaseImages(searchedImage), + repodb.Filter{}, pageInput) if err != nil { return &gql_generated.PaginatedImagesResult{}, err @@ -1259,6 +1288,7 @@ func getImageList(ctx context.Context, repo string, repoDB repodb.RepoDB, cveInf func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, pageInput) if err != nil { return &gql_generated.PaginatedImagesResult{}, err diff --git a/pkg/extensions/search/resolver_test.go b/pkg/extensions/search/resolver_test.go index fbe286a4..3f27bfe0 100644 --- a/pkg/extensions/search/resolver_test.go +++ b/pkg/extensions/search/resolver_test.go @@ -690,7 +690,7 @@ func TestImageListForDigest(t *testing.T) { Convey("getImageList", t, func() { Convey("no page requested, FilterTagsFn returns error", func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, @@ -708,7 +708,7 @@ func TestImageListForDigest(t *testing.T) { Convey("invalid manifest blob", func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -756,7 +756,7 @@ func TestImageListForDigest(t *testing.T) { manifestDigest := godigest.FromBytes(manifestBlob).String() mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -781,7 +781,7 @@ func TestImageListForDigest(t *testing.T) { } matchedTags := repos[0].Tags for tag, manifestDescriptor := range repos[0].Tags { - if !filter(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { + if !filterFunc(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetaDatas, manifestDescriptor.Digest) @@ -829,7 +829,7 @@ func TestImageListForDigest(t *testing.T) { configDigest := godigest.FromBytes(configBlob) mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -859,7 +859,7 @@ func TestImageListForDigest(t *testing.T) { matchedTags := repos[0].Tags for tag, manifestDescriptor := range repos[0].Tags { - if !filter(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { + if !filterFunc(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetaDatas, manifestDescriptor.Digest) @@ -903,7 +903,7 @@ func TestImageListForDigest(t *testing.T) { layerDigest := godigest.Digest("validDigest") mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -935,7 +935,7 @@ func TestImageListForDigest(t *testing.T) { matchedTags := repos[0].Tags for tag, manifestDescriptor := range repos[0].Tags { - if !filter(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { + if !filterFunc(repos[0], manifestMetaDatas[manifestDescriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetaDatas, manifestDescriptor.Digest) @@ -977,7 +977,7 @@ func TestImageListForDigest(t *testing.T) { So(err, ShouldBeNil) mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -1003,7 +1003,7 @@ func TestImageListForDigest(t *testing.T) { matchedTags := repo.Tags for tag, manifestDescriptor := range repo.Tags { - if !filter(repo, manifestMetaDatas[manifestDescriptor.Digest]) { + if !filterFunc(repo, manifestMetaDatas[manifestDescriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetaDatas, manifestDescriptor.Digest) @@ -1046,7 +1046,7 @@ func TestImageListForDigest(t *testing.T) { So(err, ShouldBeNil) mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, @@ -1081,7 +1081,7 @@ func TestImageListForDigest(t *testing.T) { matchedTags := repo.Tags for tag, manifestDescriptor := range repo.Tags { - if !filter(repo, manifestMetaDatas[manifestDescriptor.Digest]) { + if !filterFunc(repo, manifestMetaDatas[manifestDescriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetaDatas, manifestDescriptor.Digest) @@ -1329,7 +1329,7 @@ func TestImageList(t *testing.T) { testLogger := log.NewLogger("debug", "") Convey("no page requested, SearchRepoFn returns error", func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, @@ -1348,7 +1348,7 @@ func TestImageList(t *testing.T) { Convey("valid repoList returned", func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn: func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { repos := []repodb.RepoMetadata{ @@ -1546,7 +1546,7 @@ func TestQueryResolverErrors(t *testing.T) { resolverConfig, } - _, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.PageInput{}) + _, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{}) So(err, ShouldNotBeNil) }) @@ -1557,7 +1557,8 @@ func TestQueryResolverErrors(t *testing.T) { DefaultStore: mocks.MockedImageStore{}, }, mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -1572,7 +1573,7 @@ func TestQueryResolverErrors(t *testing.T) { resolverConfig, } - _, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.PageInput{}) + _, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{}) So(err, ShouldNotBeNil) }) @@ -1583,7 +1584,8 @@ func TestQueryResolverErrors(t *testing.T) { DefaultStore: mocks.MockedImageStore{}, }, mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -1598,7 +1600,7 @@ func TestQueryResolverErrors(t *testing.T) { resolverConfig, } - _, err := qr.ImageListWithCVEFixed(ctx, "cve1", "image", &gql_generated.PageInput{}) + _, err := qr.ImageListWithCVEFixed(ctx, "cve1", "image", &gql_generated.Filter{}, &gql_generated.PageInput{}) So(err, ShouldNotBeNil) }) @@ -1657,7 +1659,8 @@ func TestQueryResolverErrors(t *testing.T) { log, storage.StoreController{}, mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -1753,7 +1756,8 @@ func TestQueryResolverErrors(t *testing.T) { log, storage.StoreController{}, mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -1818,6 +1822,10 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo RootDir: t.TempDir(), } + LINUX := "linux" + AMD := "amd" + ARM := "arm64" + boltDriver, err := bolt.GetBoltDriver(params) if err != nil { panic(err) @@ -1836,6 +1844,10 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo configBlob1, err := json.Marshal(ispec.Image{ Created: &timeStamp1, + Platform: ispec.Platform{ + Architecture: AMD, + OS: LINUX, + }, }) if err != nil { panic(err) @@ -1875,6 +1887,10 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo configBlob2, err := json.Marshal(ispec.Image{ Created: &timeStamp2, + Platform: ispec.Platform{ + Architecture: AMD, + OS: LINUX, + }, }) if err != nil { panic(err) @@ -1914,6 +1930,10 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo configBlob3, err := json.Marshal(ispec.Image{ Created: &timeStamp3, + Platform: ispec.Platform{ + Architecture: ARM, + OS: LINUX, + }, }) if err != nil { panic(err) @@ -2165,7 +2185,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter, graphql.DefaultRecover) - images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, repoDB, log) + images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) expectedImages := []string{ @@ -2178,7 +2198,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) } - images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2192,7 +2212,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) } - images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2214,7 +2234,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput := getPageInput(1, 0) - images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages := []string{ @@ -2228,7 +2248,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(1, 1) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2242,19 +2262,19 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(1, 2) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(1, 5) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(2, 0) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2269,7 +2289,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 0) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2284,7 +2304,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 1) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2298,19 +2318,19 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 2) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(5, 5) - images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(5, 0) - images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2326,7 +2346,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 3) - images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2341,7 +2361,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 0) - images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2356,7 +2376,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 5) - images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2371,7 +2391,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 10) - images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, pageInput, repoDB, log) + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2382,6 +2402,37 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo for _, image := range images.Results { So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) } + + amdFilter := &gql_generated.Filter{Arch: []*string{&AMD}} + pageInput = getPageInput(5, 0) + + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, repoDB, log) + So(err, ShouldBeNil) + + expectedImages = []string{ + "repo1:1.0.0", "repo1:1.0.1", + "repo2:2.0.0", "repo2:2.0.1", + "repo3:3.0.1", + } + So(len(images.Results), ShouldEqual, len(expectedImages)) + + for _, image := range images.Results { + So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) + } + + pageInput = getPageInput(2, 2) + + images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, repoDB, log) + So(err, ShouldBeNil) + + expectedImages = []string{ + "repo2:2.0.0", "repo2:2.0.1", + } + So(len(images.Results), ShouldEqual, len(expectedImages)) + + for _, image := range images.Results { + So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) + } }) }) @@ -2390,7 +2441,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter, graphql.DefaultRecover) - images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, repoDB, log) + images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) expectedImages := []string{ @@ -2402,7 +2453,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) } - images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2414,7 +2465,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) } - images, err = getImageListWithCVEFixed(responseContext, "CVE3", "repo1", cveInfo, nil, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE3", "repo1", cveInfo, nil, nil, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) }) @@ -2426,7 +2477,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput := getPageInput(1, 0) - images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages := []string{ @@ -2440,7 +2491,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(1, 1) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2454,7 +2505,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(1, 2) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2468,19 +2519,19 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(1, 3) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(1, 10) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) pageInput = getPageInput(2, 0) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2494,7 +2545,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(2, 1) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2508,7 +2559,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(2, 2) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2522,7 +2573,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 0) - images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2536,7 +2587,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 0) - images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) expectedImages = []string{ @@ -2550,9 +2601,46 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo pageInput = getPageInput(5, 2) - images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, pageInput, repoDB, log) + images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, repoDB, log) So(err, ShouldBeNil) So(len(images.Results), ShouldEqual, 0) + + amdFilter := &gql_generated.Filter{Arch: []*string{&AMD}} + armFilter := &gql_generated.Filter{Arch: []*string{&ARM}} + + pageInput = getPageInput(3, 0) + + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, amdFilter, pageInput, repoDB, log) + So(err, ShouldBeNil) + + expectedImages = []string{"repo1:1.0.1"} + So(len(images.Results), ShouldEqual, len(expectedImages)) + + for _, image := range images.Results { + So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) + } + + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, armFilter, pageInput, repoDB, log) + So(err, ShouldBeNil) + + expectedImages = []string{"repo1:1.1.0", "repo1:latest"} + So(len(images.Results), ShouldEqual, len(expectedImages)) + + for _, image := range images.Results { + So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) + } + + pageInput = getPageInput(1, 1) + + images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, armFilter, pageInput, repoDB, log) + So(err, ShouldBeNil) + + expectedImages = []string{"repo1:latest"} + So(len(images.Results), ShouldEqual, len(expectedImages)) + + for _, image := range images.Results { + So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages) + } }) }) @@ -2566,6 +2654,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo }, }, nil, + nil, mocks.RepoDBMock{ GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta repodb.RepoMetadata) bool, requestedPage repodb.PageInput, @@ -2592,7 +2681,8 @@ func getPageInput(limit int, offset int) *gql_generated.PageInput { func TestDerivedImageList(t *testing.T) { Convey("RepoDB FilterTags error", t, func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -2633,7 +2723,8 @@ func TestDerivedImageList(t *testing.T) { manifestDigest := godigest.FromBytes(manifestBlob) mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -2783,7 +2874,8 @@ func TestDerivedImageList(t *testing.T) { ConfigBlob: configBlob, }, nil }, - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -2806,7 +2898,7 @@ func TestDerivedImageList(t *testing.T) { matchedTags := repo.Tags for tag, descriptor := range repo.Tags { - if !filter(repo, manifestMetas[descriptor.Digest]) { + if !filterFunc(repo, manifestMetas[descriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetas, descriptor.Digest) @@ -2861,7 +2953,8 @@ func TestDerivedImageList(t *testing.T) { func TestBaseImageList(t *testing.T) { Convey("RepoDB FilterTags error", t, func() { mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -2902,7 +2995,8 @@ func TestBaseImageList(t *testing.T) { manifestDigest := godigest.FromBytes(manifestBlob) mockSearchDB := mocks.RepoDBMock{ - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -3046,7 +3140,8 @@ func TestBaseImageList(t *testing.T) { ConfigBlob: configBlob, }, nil }, - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -3069,7 +3164,7 @@ func TestBaseImageList(t *testing.T) { matchedTags := repo.Tags for tag, descriptor := range repo.Tags { - if !filter(repo, manifestMetas[descriptor.Digest]) { + if !filterFunc(repo, manifestMetas[descriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetas, descriptor.Digest) @@ -3221,7 +3316,8 @@ func TestBaseImageList(t *testing.T) { ConfigBlob: configBlob, }, nil }, - FilterTagsFn: func(ctx context.Context, filter repodb.FilterFunc, requestedPage repodb.PageInput, + FilterTagsFn: func(ctx context.Context, + filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error, ) { @@ -3243,7 +3339,7 @@ func TestBaseImageList(t *testing.T) { matchedTags := repo.Tags for tag, descriptor := range repo.Tags { - if !filter(repo, manifestMetas[descriptor.Digest]) { + if !filterFunc(repo, manifestMetas[descriptor.Digest]) { delete(matchedTags, tag) delete(manifestMetas, descriptor.Digest) diff --git a/pkg/extensions/search/schema.graphql b/pkg/extensions/search/schema.graphql index 26aa47d8..236096b7 100644 --- a/pkg/extensions/search/schema.graphql +++ b/pkg/extensions/search/schema.graphql @@ -606,6 +606,8 @@ type Query { ImageListForCVE( "CVE ID" id: String!, + "Filter to apply before returning the results" + filter: Filter, "Sets the parameters of the requested page" requestedPage: PageInput ): PaginatedImagesResult! @@ -619,6 +621,8 @@ type Query { id: String!, "Repository name" image: String!, + "Filter to apply before returning the results" + filter: Filter, "Sets the parameters of the requested page" requestedPage: PageInput ): PaginatedImagesResult! diff --git a/pkg/extensions/search/schema.resolvers.go b/pkg/extensions/search/schema.resolvers.go index 5877674b..a73384c5 100644 --- a/pkg/extensions/search/schema.resolvers.go +++ b/pkg/extensions/search/schema.resolvers.go @@ -27,21 +27,25 @@ func (r *queryResolver) CVEListForImage(ctx context.Context, image string, reque } // ImageListForCve is the resolver for the ImageListForCVE field. -func (r *queryResolver) ImageListForCve(ctx context.Context, id string, requestedPage *gql_generated.PageInput) (*gql_generated.PaginatedImagesResult, error) { +func (r *queryResolver) ImageListForCve(ctx context.Context, id string, filter *gql_generated.Filter, requestedPage *gql_generated.PageInput) (*gql_generated.PaginatedImagesResult, error) { if r.cveInfo == nil { return &gql_generated.PaginatedImagesResult{}, zerr.ErrCVESearchDisabled } - return getImageListForCVE(ctx, id, r.cveInfo, requestedPage, r.repoDB, r.log) + filter = cleanFilter(filter) + + return getImageListForCVE(ctx, id, r.cveInfo, filter, requestedPage, r.repoDB, r.log) } // ImageListWithCVEFixed is the resolver for the ImageListWithCVEFixed field. -func (r *queryResolver) ImageListWithCVEFixed(ctx context.Context, id string, image string, requestedPage *gql_generated.PageInput) (*gql_generated.PaginatedImagesResult, error) { +func (r *queryResolver) ImageListWithCVEFixed(ctx context.Context, id string, image string, filter *gql_generated.Filter, requestedPage *gql_generated.PageInput) (*gql_generated.PaginatedImagesResult, error) { if r.cveInfo == nil { return &gql_generated.PaginatedImagesResult{}, zerr.ErrCVESearchDisabled } - return getImageListWithCVEFixed(ctx, id, image, r.cveInfo, requestedPage, r.repoDB, r.log) + filter = cleanFilter(filter) + + return getImageListWithCVEFixed(ctx, id, image, r.cveInfo, filter, requestedPage, r.repoDB, r.log) } // ImageListForDigest is the resolver for the ImageListForDigest field. diff --git a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go index 1a32e06c..c9dccb04 100644 --- a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go +++ b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper.go @@ -1294,7 +1294,7 @@ func NewManifestMetadata(manifestDigest string, repoMeta repodb.RepoMetadata, return manifestMeta } -func (bdw *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, +func (bdw *DBWrapper) FilterTags(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, zcommon.PageInfo, error, @@ -1354,7 +1354,17 @@ func (bdw *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, return fmt.Errorf("repodb: error while unmashaling manifest metadata for digest %s %w", manifestDigest, err) } - if filter(repoMeta, manifestMeta) { + imageFilterData, err := collectImageManifestFilterData(manifestDigest, repoMeta, manifestMeta) + if err != nil { + return fmt.Errorf("repodb: error collecting filter data for manifest with digest %s %w", + manifestDigest, err) + } + + if !common.AcceptedByFilter(filter, imageFilterData) { + continue + } + + if filterFunc(repoMeta, manifestMeta) { matchedTags[tag] = descriptor manifestMetadataMap[manifestDigest] = manifestMeta } @@ -1383,7 +1393,17 @@ func (bdw *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, return fmt.Errorf("repodb: error while getting manifest data for digest %s %w", manifestDigest, err) } - if filter(repoMeta, manifestMeta) { + manifestFilterData, err := collectImageManifestFilterData(manifestDigest, repoMeta, manifestMeta) + if err != nil { + return fmt.Errorf("repodb: error collecting filter data for manifest with digest %s %w", + manifestDigest, err) + } + + if !common.AcceptedByFilter(filter, manifestFilterData) { + continue + } + + if filterFunc(repoMeta, manifestMeta) { matchedManifests = append(matchedManifests, manifest) manifestMetadataMap[manifestDigest] = manifestMeta } diff --git a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go index d71d3176..6302eb25 100644 --- a/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go +++ b/pkg/meta/repodb/boltdb-wrapper/boltdb_wrapper_test.go @@ -889,6 +889,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = boltdbWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldNotBeNil) @@ -907,6 +908,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = boltdbWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldNotBeNil) @@ -946,10 +948,75 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = boltdbWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return false }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) }) + + Convey("FilterTags bad config blob in image with a single manifest", func() { + manifestDigest := digest.FromString("manifestDigestBadConfig") + + err := boltdbWrapper.SetRepoReference("repo", "tag1", //nolint:contextcheck + manifestDigest, ispec.MediaTypeImageManifest, + ) + So(err, ShouldBeNil) + + err = boltdbWrapper.SetManifestData(manifestDigest, repodb.ManifestData{ + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("bad blob"), + }) + So(err, ShouldBeNil) + + _, _, _, _, err = boltdbWrapper.FilterTags(ctx, + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, + repodb.PageInput{}, + ) + So(err, ShouldNotBeNil) + }) + + Convey("FilterTags bad config blob in index", func() { + var ( + indexDigest = digest.FromString("indexDigest") + manifestDigestFromIndex1 = digest.FromString("manifestDigestFromIndexGoodConfig") + manifestDigestFromIndex2 = digest.FromString("manifestDigestFromIndexBadConfig") + ) + + err := boltdbWrapper.SetRepoReference("repo", "tag1", //nolint:contextcheck + indexDigest, ispec.MediaTypeImageIndex, + ) + So(err, ShouldBeNil) + + indexBlob, err := test.GetIndexBlobWithManifests([]digest.Digest{ + manifestDigestFromIndex1, manifestDigestFromIndex2, + }) + So(err, ShouldBeNil) + + err = boltdbWrapper.SetIndexData(indexDigest, repodb.IndexData{ + IndexBlob: indexBlob, + }) + So(err, ShouldBeNil) + + err = boltdbWrapper.SetManifestData(manifestDigestFromIndex1, repodb.ManifestData{ + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("{}"), + }) + So(err, ShouldBeNil) + + err = boltdbWrapper.SetManifestData(manifestDigestFromIndex2, repodb.ManifestData{ + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("bad blob"), + }) + So(err, ShouldBeNil) + + _, _, _, _, err = boltdbWrapper.FilterTags(ctx, + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, + repodb.PageInput{}, + ) + So(err, ShouldNotBeNil) + }) }) Convey("ToggleStarRepo bad context errors", func() { @@ -1104,6 +1171,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = boltdbWrapper.FilterTags( ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) diff --git a/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go b/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go index 041d9f2b..176e6d4d 100644 --- a/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go +++ b/pkg/meta/repodb/dynamodb-wrapper/dynamo_test.go @@ -879,6 +879,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = dynamoWrapper.FilterTags( ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) @@ -1071,6 +1072,7 @@ func TestWrapperErrors(t *testing.T) { func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) @@ -1087,6 +1089,7 @@ func TestWrapperErrors(t *testing.T) { func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) @@ -1105,6 +1108,7 @@ func TestWrapperErrors(t *testing.T) { func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) @@ -1122,6 +1126,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = dynamoWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldNotBeNil) @@ -1140,6 +1145,7 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = dynamoWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldNotBeNil) @@ -1179,11 +1185,76 @@ func TestWrapperErrors(t *testing.T) { _, _, _, _, err = dynamoWrapper.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return false }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) }) + Convey("FilterTags bad config blob in image with a single manifest", func() { + manifestDigest := digest.FromString("manifestDigestBadConfig") + + err := dynamoWrapper.SetRepoReference( //nolint:contextcheck + "repo", "tag1", manifestDigest, ispec.MediaTypeImageManifest, + ) + So(err, ShouldBeNil) + + err = dynamoWrapper.SetManifestData(manifestDigest, repodb.ManifestData{ //nolint:contextcheck + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("bad blob"), + }) + So(err, ShouldBeNil) + + _, _, _, _, err = dynamoWrapper.FilterTags(ctx, + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, + repodb.PageInput{}, + ) + So(err, ShouldNotBeNil) + }) + + Convey("FilterTags bad config blob in index", func() { + var ( + indexDigest = digest.FromString("indexDigest") + manifestDigestFromIndex1 = digest.FromString("manifestDigestFromIndexGoodConfig") + manifestDigestFromIndex2 = digest.FromString("manifestDigestFromIndexBadConfig") + ) + + err := dynamoWrapper.SetRepoReference( //nolint:contextcheck + "repo", "tag1", indexDigest, ispec.MediaTypeImageIndex, + ) + So(err, ShouldBeNil) + + indexBlob, err := test.GetIndexBlobWithManifests([]digest.Digest{ + manifestDigestFromIndex1, manifestDigestFromIndex2, + }) + So(err, ShouldBeNil) + + err = dynamoWrapper.SetIndexData(indexDigest, repodb.IndexData{ //nolint:contextcheck + IndexBlob: indexBlob, + }) + So(err, ShouldBeNil) + + err = dynamoWrapper.SetManifestData(manifestDigestFromIndex1, repodb.ManifestData{ //nolint:contextcheck + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("{}"), + }) + So(err, ShouldBeNil) + + err = dynamoWrapper.SetManifestData(manifestDigestFromIndex2, repodb.ManifestData{ //nolint:contextcheck + ManifestBlob: []byte("{}"), + ConfigBlob: []byte("bad blob"), + }) + So(err, ShouldBeNil) + + _, _, _, _, err = dynamoWrapper.FilterTags(ctx, + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, + repodb.PageInput{}, + ) + So(err, ShouldNotBeNil) + }) + Convey("PatchDB dwr.getDBVersion errors", func() { dynamoWrapper.VersionTablename = badTablename diff --git a/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go b/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go index 3f89cb76..4055b1f1 100644 --- a/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go +++ b/pkg/meta/repodb/dynamodb-wrapper/dynamo_wrapper.go @@ -1142,7 +1142,7 @@ func (dwr *DBWrapper) collectImageIndexFilterInfo(indexDigest string, repoMeta r }, nil } -func (dwr *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, +func (dwr *DBWrapper) FilterTags(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, zcommon.PageInfo, error) { var ( @@ -1203,7 +1203,20 @@ func (dwr *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, fmt.Errorf("repodb: error while unmashaling manifest metadata for digest %s \n%w", manifestDigest, err) } - if filter(repoMeta, manifestMeta) { + imageFilterData, err := collectImageManifestFilterData(manifestDigest, repoMeta, manifestMeta) + if err != nil { + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, map[string]repodb.IndexData{}, + pageInfo, + fmt.Errorf("repodb: error collecting filter data for manifest with digest %s %w", manifestDigest, err) + } + + if !common.AcceptedByFilter(filter, imageFilterData) { + delete(matchedTags, tag) + + continue + } + + if filterFunc(repoMeta, manifestMeta) { matchedTags[tag] = descriptor manifestMetadataMap[manifestDigest] = manifestMeta } @@ -1239,7 +1252,18 @@ func (dwr *DBWrapper) FilterTags(ctx context.Context, filter repodb.FilterFunc, fmt.Errorf("%w repodb: error while getting manifest data for digest %s", err, manifestDigest) } - if filter(repoMeta, manifestMeta) { + manifestFilterData, err := collectImageManifestFilterData(manifestDigest, repoMeta, manifestMeta) + if err != nil { + return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{}, map[string]repodb.IndexData{}, + pageInfo, + fmt.Errorf("repodb: error collecting filter data for manifest with digest %s %w", manifestDigest, err) + } + + if !common.AcceptedByFilter(filter, manifestFilterData) { + continue + } + + if filterFunc(repoMeta, manifestMeta) { matchedManifests = append(matchedManifests, manifest) manifestMetadataMap[manifestDigest] = manifestMeta } diff --git a/pkg/meta/repodb/repodb.go b/pkg/meta/repodb/repodb.go index 17cc419c..f88139ea 100644 --- a/pkg/meta/repodb/repodb.go +++ b/pkg/meta/repodb/repodb.go @@ -109,7 +109,7 @@ type RepoDB interface { //nolint:interfacebloat []RepoMetadata, map[string]ManifestMetadata, map[string]IndexData, common.PageInfo, error) // FilterTags filters for images given a filter function - FilterTags(ctx context.Context, filter FilterFunc, + FilterTags(ctx context.Context, filterFunc FilterFunc, filter Filter, requestedPage PageInput) ([]RepoMetadata, map[string]ManifestMetadata, map[string]IndexData, common.PageInfo, error) PatchDB() error diff --git a/pkg/meta/repodb/repodb_test.go b/pkg/meta/repodb/repodb_test.go index 4ac1498a..a7899773 100644 --- a/pkg/meta/repodb/repodb_test.go +++ b/pkg/meta/repodb/repodb_test.go @@ -37,6 +37,7 @@ const ( LINUX = "linux" WINDOWS = "windows" AMD = "amd" + ARM = "arm64" ) func TestBoltDBWrapper(t *testing.T) { @@ -2347,6 +2348,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{Limit: 10, Offset: 0, SortBy: repodb.AlphabeticAsc}, ) @@ -2379,6 +2381,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return repoMeta.Name == repo1 }, + repodb.Filter{}, repodb.PageInput{Limit: 10, Offset: 0, SortBy: repodb.AlphabeticAsc}, ) @@ -2408,6 +2411,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return false }, + repodb.Filter{}, repodb.PageInput{Limit: 10, Offset: 0, SortBy: repodb.AlphabeticAsc}, ) @@ -2435,6 +2439,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{Limit: 10, Offset: 0, SortBy: repodb.AlphabeticAsc}, ) @@ -2454,6 +2459,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{Limit: -1}, ) So(err, ShouldNotBeNil) @@ -2461,6 +2467,220 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func }) }) + Convey("Test tags filtering by filter function and OS/Arch Filter", func() { + var ( + repo1 = "repo1" + repo2 = "repo2" + repo3 = "repo3" + repo4 = "repo4" + tag1 = "0.0.1" + tag2 = "0.0.2" + tag3 = "0.0.3" + manifestDigest1 = godigest.FromString("fake-manifest1") + manifestDigest2 = godigest.FromString("fake-manifest2") + manifestDigest3 = godigest.FromString("fake-manifest3") + + indexDigest = godigest.FromString("index-digest") + manifestFromIndexDigest1 = godigest.FromString("fake-manifestFromIndexDigest1") + manifestFromIndexDigest2 = godigest.FromString("fake-manifestFromIndexDigest2") + manifestFromIndexDigest3 = godigest.FromString("fake-manifestFromIndexDigest3") + ) + + err := repoDB.SetRepoReference(repo1, tag3, indexDigest, ispec.MediaTypeImageIndex) + So(err, ShouldBeNil) + + indexBlob, err := test.GetIndexBlobWithManifests( + []godigest.Digest{ + manifestFromIndexDigest1, + manifestFromIndexDigest2, + manifestFromIndexDigest3, + }, + ) + So(err, ShouldBeNil) + + err = repoDB.SetIndexData(indexDigest, repodb.IndexData{ + IndexBlob: indexBlob, + }) + So(err, ShouldBeNil) + + err = repoDB.SetRepoReference(repo1, tag1, manifestDigest1, ispec.MediaTypeImageManifest) + So(err, ShouldBeNil) + err = repoDB.SetRepoReference(repo1, tag2, manifestDigest2, ispec.MediaTypeImageManifest) + So(err, ShouldBeNil) + err = repoDB.SetRepoReference(repo2, tag1, manifestDigest1, ispec.MediaTypeImageManifest) + So(err, ShouldBeNil) + err = repoDB.SetRepoReference(repo3, tag1, manifestDigest2, ispec.MediaTypeImageManifest) + So(err, ShouldBeNil) + err = repoDB.SetRepoReference(repo4, tag1, manifestDigest3, ispec.MediaTypeImageManifest) + So(err, ShouldBeNil) + + config1 := ispec.Image{ + Platform: ispec.Platform{ + Architecture: AMD, + OS: LINUX, + }, + } + configBlob1, err := json.Marshal(config1) + So(err, ShouldBeNil) + + config2 := ispec.Image{ + Platform: ispec.Platform{ + Architecture: ARM, + OS: LINUX, + }, + } + configBlob2, err := json.Marshal(config2) + So(err, ShouldBeNil) + + config3 := ispec.Image{ + Platform: ispec.Platform{ + Architecture: AMD, + OS: WINDOWS, + }, + } + configBlob3, err := json.Marshal(config3) + So(err, ShouldBeNil) + + config4 := ispec.Image{} + configBlob4, err := json.Marshal(config4) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo1, manifestDigest1, repodb.ManifestMetadata{ConfigBlob: configBlob1}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo1, manifestDigest2, repodb.ManifestMetadata{ConfigBlob: configBlob2}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo2, manifestDigest1, repodb.ManifestMetadata{ConfigBlob: configBlob1}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo3, manifestDigest2, repodb.ManifestMetadata{ConfigBlob: configBlob2}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo4, manifestDigest3, repodb.ManifestMetadata{ConfigBlob: configBlob4}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo1, manifestFromIndexDigest1, + repodb.ManifestMetadata{ConfigBlob: configBlob1}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo1, manifestFromIndexDigest2, + repodb.ManifestMetadata{ConfigBlob: configBlob2}) + So(err, ShouldBeNil) + + err = repoDB.SetManifestMeta(repo1, manifestFromIndexDigest3, + repodb.ManifestMetadata{ConfigBlob: configBlob3}) + So(err, ShouldBeNil) + + opSys := LINUX + arch := AMD + filter := repodb.Filter{ + Os: []*string{&opSys}, + Arch: []*string{&arch}, + } + repos, _, _, _, err := repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 2) + So(len(repos[0].Tags), ShouldEqual, 2) + So(repos[0].Tags, ShouldContainKey, tag1) + So(repos[0].Tags, ShouldContainKey, tag3) + So(len(repos[1].Tags), ShouldEqual, 1) + So(repos[1].Tags, ShouldContainKey, tag1) + + opSys = LINUX + filter = repodb.Filter{ + Os: []*string{&opSys}, + } + repos, _, _, _, err = repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 3) + So(len(repos[0].Tags), ShouldEqual, 3) + So(repos[0].Tags, ShouldContainKey, tag1) + So(repos[0].Tags, ShouldContainKey, tag2) + So(repos[0].Tags, ShouldContainKey, tag3) + So(len(repos[1].Tags), ShouldEqual, 1) + So(repos[1].Tags, ShouldContainKey, tag1) + So(len(repos[2].Tags), ShouldEqual, 1) + So(repos[1].Tags, ShouldContainKey, tag1) + + opSys = WINDOWS + filter = repodb.Filter{ + Os: []*string{&opSys}, + } + repos, _, _, _, err = repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 1) + So(len(repos[0].Tags), ShouldEqual, 1) + So(repos[0].Tags, ShouldContainKey, tag3) + + arch = AMD + filter = repodb.Filter{ + Arch: []*string{&arch}, + } + repos, _, _, _, err = repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 2) + So(len(repos[0].Tags), ShouldEqual, 2) + So(repos[0].Tags, ShouldContainKey, tag1) + So(repos[0].Tags, ShouldContainKey, tag3) + So(len(repos[1].Tags), ShouldEqual, 1) + So(repos[1].Tags, ShouldContainKey, tag1) + + repos, _, _, _, err = repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + repodb.Filter{}, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 4) + So(len(repos[0].Tags), ShouldEqual, 3) + So(repos[0].Tags, ShouldContainKey, tag1) + So(repos[0].Tags, ShouldContainKey, tag2) + So(repos[0].Tags, ShouldContainKey, tag3) + So(len(repos[1].Tags), ShouldEqual, 1) + So(repos[1].Tags, ShouldContainKey, tag1) + So(len(repos[2].Tags), ShouldEqual, 1) + So(repos[2].Tags, ShouldContainKey, tag1) + So(len(repos[3].Tags), ShouldEqual, 1) + So(repos[3].Tags, ShouldContainKey, tag1) + + opSys = LINUX + arch = "badArch" + filter = repodb.Filter{ + Os: []*string{&opSys}, + Arch: []*string{&arch}, + } + repos, _, _, _, err = repoDB.FilterTags(context.TODO(), + func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { + return true + }, + filter, + repodb.PageInput{SortBy: repodb.AlphabeticAsc}) + So(err, ShouldBeNil) + So(len(repos), ShouldEqual, 0) + }) + Convey("Test index logic", func() { multiArch, err := test.GetRandomMultiarchImage("tag1") So(err, ShouldBeNil) @@ -2745,6 +2965,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func repoMetas, _, _, _, err = repoDB.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) @@ -2780,6 +3001,7 @@ func RunRepoDBTests(t *testing.T, repoDB repodb.RepoDB, preparationFuncs ...func repoMetas, _, _, _, err = repoDB.FilterTags(ctx, func(repoMeta repodb.RepoMetadata, manifestMeta repodb.ManifestMetadata) bool { return true }, + repodb.Filter{}, repodb.PageInput{}, ) So(err, ShouldBeNil) diff --git a/pkg/test/mocks/repo_db_mock.go b/pkg/test/mocks/repo_db_mock.go index 99cd9512..1b38cf97 100644 --- a/pkg/test/mocks/repo_db_mock.go +++ b/pkg/test/mocks/repo_db_mock.go @@ -71,7 +71,7 @@ type RepoDBMock struct { FilterReposFn func(ctx context.Context, filter repodb.FilterRepoFunc, requestedPage repodb.PageInput) ( []repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) - FilterTagsFn func(ctx context.Context, filter repodb.FilterFunc, + FilterTagsFn func(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) @@ -301,11 +301,11 @@ func (sdm RepoDBMock) FilterRepos(ctx context.Context, filter repodb.FilterRepoF map[string]repodb.IndexData{}, common.PageInfo{}, nil } -func (sdm RepoDBMock) FilterTags(ctx context.Context, filter repodb.FilterFunc, +func (sdm RepoDBMock) FilterTags(ctx context.Context, filterFunc repodb.FilterFunc, filter repodb.Filter, requestedPage repodb.PageInput, ) ([]repodb.RepoMetadata, map[string]repodb.ManifestMetadata, map[string]repodb.IndexData, common.PageInfo, error) { if sdm.FilterTagsFn != nil { - return sdm.FilterTagsFn(ctx, filter, requestedPage) + return sdm.FilterTagsFn(ctx, filterFunc, filter, requestedPage) } return []repodb.RepoMetadata{}, map[string]repodb.ManifestMetadata{},