mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
f618b1d4ef
* ci(deps): upgrade golangci-lint
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
* build(deps): removed disabled linters
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
* build(deps): go run github.com/daixiang0/gci@latest write .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run golang.org/x/tools/cmd/goimports@latest -l -w .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/bombsimon/wsl/v4/cmd...@latest -strict-append -test=true -fix ./...
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/catenacyber/perfsprint@latest -fix ./...
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): replace gomnd by mnd
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): make gqlgen
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build: Revert "build(deps): go run github.com/daixiang0/gci@latest write ."
This reverts commit 5bf8c42e1f
.
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): go run github.com/daixiang0/gci@latest write -s 'standard' -s default -s 'prefix(zotregistry.dev/zot)' .
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* build(deps): make gqlgen
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: wsl issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: check-log issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: gci issues
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
* fix: tests
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
---------
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
Signed-off-by: Jan-Otto Kröpke <joe@cloudeteer.de>
2741 lines
88 KiB
Go
2741 lines
88 KiB
Go
//go:build search
|
|
|
|
package search //nolint
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/99designs/gqlgen/graphql"
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
zerr "zotregistry.dev/zot/errors"
|
|
"zotregistry.dev/zot/pkg/common"
|
|
"zotregistry.dev/zot/pkg/extensions/search/convert"
|
|
cveinfo "zotregistry.dev/zot/pkg/extensions/search/cve"
|
|
cvemodel "zotregistry.dev/zot/pkg/extensions/search/cve/model"
|
|
"zotregistry.dev/zot/pkg/extensions/search/gql_generated"
|
|
"zotregistry.dev/zot/pkg/log"
|
|
"zotregistry.dev/zot/pkg/meta/boltdb"
|
|
mConvert "zotregistry.dev/zot/pkg/meta/convert"
|
|
mTypes "zotregistry.dev/zot/pkg/meta/types"
|
|
reqCtx "zotregistry.dev/zot/pkg/requestcontext"
|
|
"zotregistry.dev/zot/pkg/storage"
|
|
. "zotregistry.dev/zot/pkg/test/image-utils"
|
|
"zotregistry.dev/zot/pkg/test/mocks"
|
|
ociutils "zotregistry.dev/zot/pkg/test/oci-utils"
|
|
)
|
|
|
|
var ErrTestError = errors.New("TestError")
|
|
|
|
func TestResolverGlobalSearch(t *testing.T) {
|
|
Convey("globalSearch", t, func() {
|
|
const query = "repo1"
|
|
|
|
Convey("MetaDB SearchRepos error", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
mockCve := mocks.CveInfoMock{}
|
|
repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("paginated fail", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, _, _, err := globalSearch(responseContext, "repo", mocks.MetaDBMock{}, &gql_generated.Filter{},
|
|
pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
|
|
_, _, _, err = globalSearch(responseContext, "repo:tag", mocks.MetaDBMock{}, &gql_generated.Filter{},
|
|
pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("MetaDB SearchTags gives error", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
SearchTagsFn: func(ctx context.Context, searchText string) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
const query = "repo1:1.0.1"
|
|
mockCve := mocks.CveInfoMock{}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Searching by digest", func() {
|
|
ctx := context.Background()
|
|
query := "sha256:aabb12341baf2"
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
|
|
filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{}, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Searching by digest with bad pagination", func() {
|
|
ctx := context.Background()
|
|
query := "sha256:aabb12341baf2"
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mocks.MetaDBMock{}, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{Limit: ref(-10)}, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Searching by tag returns a filter error", func() {
|
|
ctx := context.Background()
|
|
query := ":test"
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
|
|
filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mockMetaDB, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{}, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Searching by tag returns a pagination error", func() {
|
|
ctx := context.Background()
|
|
query := ":test"
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mocks.MetaDBMock{}, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{Limit: ref(-10)}, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("Searching with a bad query", func() {
|
|
ctx := context.Background()
|
|
query := ":"
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
|
repos, images, layers, err := globalSearch(responseContext, query, mocks.MetaDBMock{}, &gql_generated.Filter{},
|
|
&gql_generated.PageInput{}, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images, ShouldBeEmpty)
|
|
So(layers, ShouldBeEmpty)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestRepoListWithNewestImage(t *testing.T) {
|
|
Convey("RepoListWithNewestImage", t, func() {
|
|
Convey("MetaDB SearchRepos error", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
mockCve := mocks.CveInfoMock{}
|
|
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaUpdateTime),
|
|
}
|
|
repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), &pageInput, mockMetaDB)
|
|
So(err, ShouldNotBeNil)
|
|
So(repos.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("paginated fail", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := repoListWithNewestImage(responseContext, mocks.CveInfoMock{}, log.NewLogger("debug", ""),
|
|
pageInput, mocks.MetaDBMock{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Working SearchRepo function", func() {
|
|
createTime := time.Now()
|
|
createTime2 := createTime.Add(time.Second)
|
|
img1 := CreateImageWith().DefaultLayers().
|
|
ImageConfig(ispec.Image{
|
|
Config: ispec.ImageConfig{
|
|
Labels: map[string]string{},
|
|
},
|
|
Created: &createTime,
|
|
}).Build()
|
|
|
|
img2 := CreateImageWith().DefaultLayers().
|
|
ImageConfig(ispec.Image{
|
|
Config: ispec.ImageConfig{
|
|
Labels: map[string]string{},
|
|
},
|
|
Created: &createTime2,
|
|
}).Build()
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string) ([]mTypes.RepoMeta, error) {
|
|
repos := []mTypes.RepoMeta{
|
|
{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {
|
|
Digest: img1.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
},
|
|
Signatures: map[mTypes.ImageDigest]mTypes.ManifestSignatures{
|
|
img1.DigestStr(): {
|
|
"cosign": []mTypes.SignatureInfo{
|
|
{SignatureManifestDigest: "testSignature", LayersInfo: []mTypes.LayerInfo{}},
|
|
},
|
|
},
|
|
},
|
|
StarCount: 100,
|
|
LastUpdatedImage: &mTypes.LastUpdatedImage{
|
|
Descriptor: mTypes.Descriptor{
|
|
Digest: img1.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Tag: "1.0.1",
|
|
LastUpdated: &createTime,
|
|
},
|
|
},
|
|
{
|
|
Name: "repo2",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.2": {
|
|
Digest: img2.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
},
|
|
Signatures: map[mTypes.ImageDigest]mTypes.ManifestSignatures{
|
|
img1.DigestStr(): {
|
|
"cosign": []mTypes.SignatureInfo{
|
|
{SignatureManifestDigest: "testSignature", LayersInfo: []mTypes.LayerInfo{}},
|
|
},
|
|
},
|
|
},
|
|
StarCount: 100,
|
|
LastUpdatedImage: &mTypes.LastUpdatedImage{
|
|
Descriptor: mTypes.Descriptor{
|
|
Digest: img2.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Tag: "1.0.2",
|
|
LastUpdated: &createTime2,
|
|
},
|
|
},
|
|
}
|
|
|
|
return repos, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string,
|
|
) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{
|
|
img1.DigestStr(): mConvert.GetImageManifestMeta(img1.Manifest, img1.Config,
|
|
img1.ManifestDescriptor.Size, img1.ManifestDescriptor.Digest),
|
|
img2.DigestStr(): mConvert.GetImageManifestMeta(img2.Manifest, img2.Config,
|
|
img2.ManifestDescriptor.Size, img2.ManifestDescriptor.Digest),
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
Convey("MetaDB missing requestedPage", func() {
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
mockCve := mocks.CveInfoMock{}
|
|
repos, err := repoListWithNewestImage(responseContext, mockCve, log.NewLogger("debug", ""), nil, mockMetaDB)
|
|
So(err, ShouldBeNil)
|
|
So(repos.Results, ShouldNotBeEmpty)
|
|
})
|
|
|
|
Convey("MetaDB SearchRepo is successful", func() {
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(2),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaUpdateTime),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
repos, err := repoListWithNewestImage(responseContext, mockCve,
|
|
log.NewLogger("debug", ""), &pageInput, mockMetaDB)
|
|
So(err, ShouldBeNil)
|
|
So(repos, ShouldNotBeEmpty)
|
|
So(len(repos.Results), ShouldEqual, 2)
|
|
So(*repos.Results[0].Name, ShouldEqual, "repo2")
|
|
So(*repos.Results[0].LastUpdated, ShouldEqual, createTime2)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetFilteredPaginatedRepos(t *testing.T) {
|
|
ctx := context.Background()
|
|
log := log.NewLogger("debug", "")
|
|
|
|
Convey("getFilteredPaginatedRepos", t, func() {
|
|
metaDB := mocks.MetaDBMock{}
|
|
|
|
Convey("FilterRepos", func() {
|
|
metaDB.FilterReposFn = func(ctx context.Context, rankName mTypes.FilterRepoNameFunc,
|
|
filterFunc mTypes.FilterFullRepoFunc,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return nil, ErrTestError
|
|
}
|
|
_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
|
|
&gql_generated.PageInput{}, metaDB)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("FilterImageMeta", func() {
|
|
metaDB.FilterImageMetaFn = func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return nil, ErrTestError
|
|
}
|
|
_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
|
|
&gql_generated.PageInput{}, metaDB)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("PaginatedRepoMeta2RepoSummaries", func() {
|
|
_, err := getFilteredPaginatedRepos(ctx, nil, func(repo string) bool { return true }, log,
|
|
&gql_generated.PageInput{Limit: ref(-10)}, metaDB)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetBookmarkedRepos(t *testing.T) {
|
|
Convey("getBookmarkedRepos", t, func() {
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
_, err := getBookmarkedRepos(
|
|
responseContext,
|
|
mocks.CveInfoMock{},
|
|
log.NewLogger("debug", ""),
|
|
nil,
|
|
mocks.MetaDBMock{
|
|
GetBookmarkedReposFn: func(ctx context.Context) ([]string, error) {
|
|
return []string{}, ErrTestError
|
|
},
|
|
},
|
|
)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestGetStarredRepos(t *testing.T) {
|
|
Convey("getStarredRepos", t, func() {
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
_, err := getStarredRepos(
|
|
responseContext,
|
|
mocks.CveInfoMock{},
|
|
log.NewLogger("debug", ""),
|
|
nil,
|
|
mocks.MetaDBMock{
|
|
GetStarredReposFn: func(ctx context.Context) ([]string, error) {
|
|
return []string{}, ErrTestError
|
|
},
|
|
},
|
|
)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func getTestRepoMetaWithImages(repo string, images []Image) mTypes.RepoMeta {
|
|
tags := map[mTypes.Tag]mTypes.Descriptor{"": {}}
|
|
statistics := map[mTypes.Tag]mTypes.DescriptorStatistics{"": {}}
|
|
signatures := map[mTypes.ImageDigest]mTypes.ManifestSignatures{"": {}}
|
|
referrers := map[string][]mTypes.ReferrerInfo{"": {}}
|
|
|
|
for i := range images {
|
|
tags[images[i].DigestStr()] = mTypes.Descriptor{}
|
|
statistics[images[i].DigestStr()] = mTypes.DescriptorStatistics{}
|
|
signatures[images[i].DigestStr()] = mTypes.ManifestSignatures{}
|
|
referrers[images[i].DigestStr()] = []mTypes.ReferrerInfo{}
|
|
}
|
|
|
|
return mTypes.RepoMeta{
|
|
Name: repo,
|
|
Tags: tags,
|
|
Statistics: statistics,
|
|
Signatures: signatures,
|
|
Referrers: referrers,
|
|
}
|
|
}
|
|
|
|
func TestImageListForDigest(t *testing.T) {
|
|
Convey("getImageList", t, func() {
|
|
Convey("no page requested, FilterTagsFn returns error", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
|
|
filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
_, err := getImageListForDigest(responseContext, "invalid", mockMetaDB, mocks.CveInfoMock{}, nil)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Paginated convert fails", func() {
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
_, err := getImageListForDigest(responseContext, "invalid", mocks.MetaDBMock{}, mocks.CveInfoMock{},
|
|
&gql_generated.PageInput{Limit: ref(-1)})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("valid imageListForDigest returned for matching manifest digest", func() {
|
|
img1, img2 := CreateRandomImage(), CreateRandomImage()
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
|
|
filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
fullImageMetaList := []mTypes.ImageMeta{img1.AsImageMeta(), img2.AsImageMeta()}
|
|
repoMeta := getTestRepoMetaWithImages("repo", []Image{img1, img2})
|
|
tags := []string{"tag1", "tag2"}
|
|
|
|
acceptedImages := []mTypes.FullImageMeta{}
|
|
|
|
for i := range fullImageMetaList {
|
|
if filterFunc(repoMeta, fullImageMetaList[i]) {
|
|
acceptedImages = append(acceptedImages,
|
|
convert.GetFullImageMeta(tags[i], repoMeta, fullImageMetaList[i]))
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
return acceptedImages, nil
|
|
},
|
|
}
|
|
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
imageSummaries, err := getImageListForDigest(responseContext, img1.DigestStr(),
|
|
mockMetaDB, mocks.CveInfoMock{}, &pageInput)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 1)
|
|
|
|
imageSummaries, err = getImageListForDigest(responseContext, "invalid",
|
|
mockMetaDB, mocks.CveInfoMock{}, &pageInput)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 0)
|
|
|
|
imageSummaries, err = getImageListForDigest(responseContext, img1.Manifest.Config.Digest.String(),
|
|
mockMetaDB, mocks.CveInfoMock{}, &pageInput)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 1)
|
|
|
|
imageSummaries, err = getImageListForDigest(responseContext, img1.Manifest.Layers[0].Digest.String(),
|
|
mockMetaDB, mocks.CveInfoMock{}, &pageInput)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 1)
|
|
})
|
|
|
|
Convey("valid imageListForDigest, multiple matching tags", func() {
|
|
img1 := CreateRandomImage()
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc,
|
|
filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
fullImageMetaList := []mTypes.ImageMeta{img1.AsImageMeta()}
|
|
repoMeta := getTestRepoMetaWithImages("repo", []Image{img1, img1})
|
|
tags := []string{"tag1", "tag2"}
|
|
|
|
acceptedImages := []mTypes.FullImageMeta{}
|
|
|
|
for i := range fullImageMetaList {
|
|
if filterFunc(repoMeta, fullImageMetaList[i]) {
|
|
acceptedImages = append(acceptedImages,
|
|
convert.GetFullImageMeta(tags[i], repoMeta, fullImageMetaList[i]))
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
return acceptedImages, nil
|
|
},
|
|
}
|
|
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
imageSummaries, err := getImageListForDigest(responseContext, img1.DigestStr(),
|
|
mockMetaDB, mocks.CveInfoMock{}, &pageInput)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 1)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetImageSummaryError(t *testing.T) {
|
|
Convey("getImageSummary", t, func() {
|
|
metaDB := mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{Tags: map[mTypes.Tag]mTypes.Descriptor{"tag": {}}}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
}
|
|
log := log.NewLogger("debug", "")
|
|
|
|
_, err := getImageSummary(context.Background(), "repo", "tag", nil, convert.SkipQGLField{},
|
|
metaDB, nil, log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
}
|
|
|
|
func TestImageListError(t *testing.T) {
|
|
Convey("getImageList", t, func() {
|
|
testLogger := log.NewLogger("debug", "/dev/null")
|
|
|
|
Convey("no page requested, SearchRepoFn returns error", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := getImageList(responseContext, "test", mockMetaDB, mocks.CveInfoMock{}, nil, testLogger)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Paginated convert fails", func() {
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := getImageList(responseContext, "test", mocks.MetaDBMock{}, mocks.CveInfoMock{},
|
|
&gql_generated.PageInput{Limit: ref(-1)}, log.NewLogger("debug", ""))
|
|
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("valid repoList returned", func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
repoName := "correct-repo"
|
|
|
|
if !filterRepoTag(repoName, "tag") {
|
|
return []mTypes.FullImageMeta{}, nil
|
|
}
|
|
|
|
image := CreateDefaultImage()
|
|
repoMeta := mTypes.RepoMeta{
|
|
Name: "repo",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{image.DigestStr(): {
|
|
Digest: image.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
}},
|
|
}
|
|
|
|
return []mTypes.FullImageMeta{convert.GetFullImageMeta("tag", repoMeta, image.AsImageMeta())}, nil
|
|
},
|
|
}
|
|
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
imageSummaries, err := getImageList(responseContext, "correct-repo", mockMetaDB,
|
|
mocks.CveInfoMock{}, &pageInput, testLogger)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 1)
|
|
|
|
imageSummaries, err = getImageList(responseContext, "invalid", mockMetaDB,
|
|
mocks.CveInfoMock{}, &pageInput, testLogger)
|
|
So(err, ShouldBeNil)
|
|
So(len(imageSummaries.Results), ShouldEqual, 0)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetReferrers(t *testing.T) {
|
|
Convey("getReferrers", t, func() {
|
|
referredDigest := godigest.FromString("t").String()
|
|
|
|
Convey("referredDigest is empty", func() {
|
|
testLogger := log.NewLogger("debug", "")
|
|
|
|
_, err := getReferrers(mocks.MetaDBMock{}, "test", "", nil, testLogger)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetReferrers returns error", func() {
|
|
testLogger := log.NewLogger("debug", "")
|
|
mockedStore := mocks.MetaDBMock{
|
|
GetReferrersInfoFn: func(repo string, referredDigest godigest.Digest, artifactTypes []string,
|
|
) ([]mTypes.ReferrerInfo, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
}
|
|
|
|
_, err := getReferrers(mockedStore, "test", referredDigest, nil, testLogger)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetReferrers return index of descriptors", func() {
|
|
testLogger := log.NewLogger("debug", "")
|
|
referrerDescriptor := ispec.Descriptor{
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
ArtifactType: "com.artifact.test",
|
|
Size: 403,
|
|
Digest: godigest.FromString("test"),
|
|
Annotations: map[string]string{
|
|
"key": "value",
|
|
},
|
|
}
|
|
mockedStore := mocks.MetaDBMock{
|
|
GetReferrersInfoFn: func(repo string, referredDigest godigest.Digest, artifactTypes []string,
|
|
) ([]mTypes.ReferrerInfo, error) {
|
|
return []mTypes.ReferrerInfo{
|
|
{
|
|
Digest: referrerDescriptor.Digest.String(),
|
|
MediaType: referrerDescriptor.MediaType,
|
|
ArtifactType: referrerDescriptor.ArtifactType,
|
|
Size: int(referrerDescriptor.Size),
|
|
Annotations: referrerDescriptor.Annotations,
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
referrers, err := getReferrers(mockedStore, "test", referredDigest, nil, testLogger)
|
|
So(err, ShouldBeNil)
|
|
So(*referrers[0].ArtifactType, ShouldEqual, referrerDescriptor.ArtifactType)
|
|
So(*referrers[0].MediaType, ShouldEqual, referrerDescriptor.MediaType)
|
|
So(*referrers[0].Size, ShouldEqual, referrerDescriptor.Size)
|
|
So(*referrers[0].Digest, ShouldEqual, referrerDescriptor.Digest.String())
|
|
So(*referrers[0].Annotations[0].Value, ShouldEqual, referrerDescriptor.Annotations["key"])
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestQueryResolverErrors(t *testing.T) {
|
|
Convey("Errors", t, func() {
|
|
log := log.NewLogger("debug", "")
|
|
ctx := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
Convey("GlobalSearch error bad requested page", func() {
|
|
resolverConfig := NewResolver(log, storage.StoreController{}, mocks.MetaDBMock{}, mocks.CveInfoMock{})
|
|
resolver := queryResolver{resolverConfig}
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
_, err := resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, &pageInput)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
pageInput = gql_generated.PageInput{
|
|
Limit: ref(0),
|
|
Offset: ref(-1),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
_, err = resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, &pageInput)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GlobalSearch error filte image meta", func() {
|
|
resolverConfig := NewResolver(log, storage.StoreController{}, mocks.MetaDBMock{
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
}, mocks.CveInfoMock{})
|
|
resolver := queryResolver{resolverConfig}
|
|
|
|
_, err := resolver.GlobalSearch(ctx, "some_string", &gql_generated.Filter{}, getGQLPageInput(1, 1))
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CVEDiffListForImages nill cveinfo", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
},
|
|
nil,
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.CVEDiffListForImages(ctx, gql_generated.ImageInput{}, gql_generated.ImageInput{},
|
|
&gql_generated.PageInput{}, nil, nil)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CVEDiffListForImages error", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.CVEDiffListForImages(ctx, gql_generated.ImageInput{}, gql_generated.ImageInput{},
|
|
&gql_generated.PageInput{}, nil, nil)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("ImageListForCve error in GetMultipleRepoMeta", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("ImageListForCve error in FilterTags", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.ImageListForCve(ctx, "cve1", &gql_generated.Filter{}, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("ImageListWithCVEFixed error in FilterTags", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.ImageListWithCVEFixed(ctx, "cve1", "image", &gql_generated.Filter{}, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("RepoListWithNewestImage repoListWithNewestImage() filter image meta error", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("RepoListWithNewestImage repoListWithNewestImage() errors mocked StoreController", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{},
|
|
},
|
|
mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("RepoListWithNewestImage repoListWithNewestImage() errors valid StoreController", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{},
|
|
mocks.MetaDBMock{
|
|
SearchReposFn: func(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.RepoListWithNewestImage(ctx, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("ImageList getImageList() errors", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{},
|
|
mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.ImageList(ctx, "repo", &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("DerivedImageList ExpandedRepoInfo() errors", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{
|
|
GetRepositoriesFn: func() ([]string, error) {
|
|
return []string{"sub1/repo"}, nil
|
|
},
|
|
GetImageManifestFn: func(repo, reference string) ([]byte, godigest.Digest, string, error) {
|
|
return []byte("{}"), "digest", "str", nil
|
|
},
|
|
},
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.DerivedImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("BaseImageList ExpandedRepoInfo() errors", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{
|
|
GetRepositoriesFn: func() ([]string, error) {
|
|
return []string{"sub1/repo"}, nil
|
|
},
|
|
GetImageManifestFn: func(repo, reference string) ([]byte, godigest.Digest, string, error) {
|
|
return []byte("{}"), "digest", "str", nil
|
|
},
|
|
},
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.BaseImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("DerivedImageList and BaseImage List FilterTags() errors", func() {
|
|
image := CreateDefaultImage()
|
|
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{},
|
|
mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"tag": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return image.AsImageMeta(), nil
|
|
},
|
|
},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
resolver := queryResolver{resolverConfig}
|
|
|
|
_, err := resolver.DerivedImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
|
|
_, err = resolver.BaseImageList(ctx, "repo:tag", nil, &gql_generated.PageInput{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetReferrers error", func() {
|
|
resolverConfig := NewResolver(
|
|
log,
|
|
storage.StoreController{
|
|
DefaultStore: mocks.MockedImageStore{
|
|
GetReferrersFn: func(repo string, digest godigest.Digest, artifactTypes []string) (ispec.Index, error) {
|
|
return ispec.Index{}, ErrTestError
|
|
},
|
|
},
|
|
},
|
|
mocks.MetaDBMock{},
|
|
mocks.CveInfoMock{},
|
|
)
|
|
|
|
qr := queryResolver{resolverConfig}
|
|
|
|
_, err := qr.Referrers(ctx, "repo", "", nil)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
|
|
ctx := context.Background()
|
|
log := log.NewLogger("debug", "")
|
|
LINUX := "linux"
|
|
AMD := "amd"
|
|
ARM := "arm64"
|
|
|
|
boltDriver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: t.TempDir()})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
metaDB, err := boltdb.New(boltDriver, log)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
image1 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
|
|
Created: DateRef(2008, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
Platform: ispec.Platform{
|
|
Architecture: AMD,
|
|
OS: LINUX,
|
|
},
|
|
}).Build()
|
|
digest1 := image1.Digest()
|
|
|
|
image2 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
|
|
Created: DateRef(2009, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
Platform: ispec.Platform{
|
|
Architecture: AMD,
|
|
OS: LINUX,
|
|
},
|
|
}).Build()
|
|
digest2 := image2.Digest()
|
|
|
|
image3 := CreateImageWith().RandomLayers(5, 2).ImageConfig(ispec.Image{
|
|
Created: DateRef(2010, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
Platform: ispec.Platform{
|
|
Architecture: ARM,
|
|
OS: LINUX,
|
|
},
|
|
}).Build()
|
|
digest3 := image3.Digest()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB,
|
|
ociutils.Repo{
|
|
Name: "repo1", Images: []ociutils.RepoImage{
|
|
{Image: image1, Reference: "1.0.0"},
|
|
{Image: image2, Reference: "1.0.1"},
|
|
{Image: image3, Reference: "1.1.0"},
|
|
{Image: image3, Reference: "latest"},
|
|
},
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo2", Images: []ociutils.RepoImage{
|
|
{Image: image1, Reference: "2.0.0"},
|
|
{Image: image2, Reference: "2.0.1"},
|
|
{Image: image3, Reference: "2.1.0"},
|
|
{Image: image3, Reference: "latest"},
|
|
},
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo3", Images: []ociutils.RepoImage{
|
|
{Image: image2, Reference: "3.0.1"},
|
|
{Image: image3, Reference: "3.1.0"},
|
|
{Image: image3, Reference: "latest"},
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
getCveResults := func(digestStr string) map[string]cvemodel.CVE {
|
|
if digestStr == digest1.String() {
|
|
return map[string]cvemodel.CVE{
|
|
"CVE1": {
|
|
ID: "CVE1",
|
|
Severity: "HIGH",
|
|
Title: "Title CVE1",
|
|
Description: "Description CVE1",
|
|
},
|
|
"CVE2": {
|
|
ID: "CVE2",
|
|
Severity: "MEDIUM",
|
|
Title: "Title CVE2",
|
|
Description: "Description CVE2",
|
|
},
|
|
"CVE3": {
|
|
ID: "CVE3",
|
|
Severity: "LOW",
|
|
Title: "Title CVE3",
|
|
Description: "Description CVE3",
|
|
},
|
|
"CVE34": {
|
|
ID: "CVE34",
|
|
Severity: "LOW",
|
|
Title: "Title for CVE34",
|
|
Description: "Description CVE34",
|
|
},
|
|
}
|
|
}
|
|
|
|
if digestStr == digest2.String() {
|
|
return map[string]cvemodel.CVE{
|
|
"CVE2": {
|
|
ID: "CVE2",
|
|
Severity: "MEDIUM",
|
|
Title: "Title CVE2",
|
|
Description: "Description CVE2",
|
|
},
|
|
"CVE3": {
|
|
ID: "CVE3",
|
|
Severity: "LOW",
|
|
Title: "Title CVE3",
|
|
Description: "Description CVE3",
|
|
},
|
|
}
|
|
}
|
|
|
|
if digestStr == digest3.String() {
|
|
return map[string]cvemodel.CVE{
|
|
"CVE3": {
|
|
ID: "CVE3",
|
|
Severity: "LOW",
|
|
Title: "Title CVE3",
|
|
Description: "Description CVE3",
|
|
},
|
|
}
|
|
}
|
|
|
|
// By default the image has no vulnerabilities
|
|
return map[string]cvemodel.CVE{}
|
|
}
|
|
|
|
// MetaDB loaded with initial data, now mock the scanner
|
|
// Setup test CVE data in mock scanner
|
|
scanner := mocks.CveScannerMock{
|
|
ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) {
|
|
repo, ref, _, _ := common.GetRepoReference(image)
|
|
|
|
if common.IsDigest(ref) {
|
|
return getCveResults(ref), nil
|
|
}
|
|
|
|
repoMeta, _ := metaDB.GetRepoMeta(ctx, repo)
|
|
|
|
if _, ok := repoMeta.Tags[ref]; !ok {
|
|
panic("unexpected tag '" + ref + "', test might be wrong")
|
|
}
|
|
|
|
return getCveResults(repoMeta.Tags[ref].Digest), nil
|
|
},
|
|
GetCachedResultFn: func(digestStr string) map[string]cvemodel.CVE {
|
|
return getCveResults(digestStr)
|
|
},
|
|
IsResultCachedFn: func(digestStr string) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
cveInfo := &cveinfo.BaseCveInfo{
|
|
Log: log,
|
|
Scanner: scanner,
|
|
MetaDB: metaDB,
|
|
}
|
|
|
|
Convey("Get CVE list for image ", t, func() {
|
|
Convey("Unpaginated request to get all CVEs in an image", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
dig := godigest.FromString("dig")
|
|
repoWithDigestRef := fmt.Sprintf("repo@%s", dig)
|
|
|
|
_, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, "", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
|
|
cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves := []string{"CVE1", "CVE2", "CVE3", "CVE34"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
// test searching CVE by id in results
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE3", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves = []string{"CVE3", "CVE34"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
// test searching CVE by id in results - no matches
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE100", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
So(len(cveResult.CVEList), ShouldEqual, 0)
|
|
|
|
// test searching CVE by id in results - partial name
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "VE3", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves = []string{"CVE3", "CVE34"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
// test searching CVE by title in results
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves = []string{"CVE1", "CVE2", "CVE3"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.1", cveInfo, pageInput, "", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.1")
|
|
|
|
expectedCves = []string{"CVE2", "CVE3"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.1.0")
|
|
|
|
expectedCves = []string{"CVE3"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
})
|
|
|
|
Convey("Unpaginated request to get all CVEs in an image excluding some", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE",
|
|
"Title CVE2", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves := []string{"CVE1", "CVE3"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Description",
|
|
"Description CVE2", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves = []string{"CVE1", "CVE3", "CVE34"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
})
|
|
|
|
Convey("Unpaginated request to get all CVEs in an image filtered by severity", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "",
|
|
"", "HIGH", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves := []string{"CVE1"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
|
|
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Description",
|
|
"Description CVE2", "LOW", log)
|
|
So(err, ShouldBeNil)
|
|
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
|
|
|
expectedCves = []string{"CVE3", "CVE34"}
|
|
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
|
|
|
for _, cve := range cveResult.CVEList {
|
|
So(expectedCves, ShouldContain, *cve.ID)
|
|
}
|
|
})
|
|
|
|
Convey("paginated fail", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
|
|
Convey("Get a list of images affected by a particular CVE ", t, func() {
|
|
Convey("Unpaginated request", func() {
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, nil, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages := []string{
|
|
"repo1:1.0.0",
|
|
"repo2:2.0.0",
|
|
}
|
|
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 = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, nil, metaDB, 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)
|
|
}
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, nil, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.0", "repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
|
|
"repo2:2.0.0", "repo2:2.0.1", "repo2:2.1.0", "repo2:latest",
|
|
"repo3:3.0.1", "repo3:3.1.0", "repo3:latest",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
})
|
|
|
|
Convey("paginated fail", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err = getImageListForCVE(responseContext, "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
|
|
pageInput, mocks.MetaDBMock{}, log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("context done", func() {
|
|
pageInput := getGQLPageInput(1, 0)
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
cancel()
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
canceledScanner := scanner
|
|
|
|
canceledScanner.ScanImageFn = func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) {
|
|
return nil, ctx.Err()
|
|
}
|
|
|
|
cveInfo.Scanner = canceledScanner
|
|
|
|
defer func() {
|
|
cveInfo.Scanner = scanner
|
|
}()
|
|
|
|
_, err = getImageListForCVE(responseContext, "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
|
|
pageInput, metaDB, log)
|
|
So(err, ShouldEqual, ctx.Err())
|
|
})
|
|
|
|
Convey("Paginated requests", func() {
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover,
|
|
)
|
|
|
|
pageInput := getGQLPageInput(1, 0)
|
|
|
|
images, err := getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages := []string{
|
|
"repo1:1.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(1, 1)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo2:2.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(1, 2)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(1, 5)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(2, 0)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.0",
|
|
"repo2:2.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.0",
|
|
"repo2:2.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 1)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo2:2.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 2)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(5, 5)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, metaDB, 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 = getGQLPageInput(5, 3)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE2", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"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 = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.0", "repo1:1.0.1", "repo1:1.1.0", "repo1:latest",
|
|
"repo2:2.0.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 5)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo2:2.0.1", "repo2:2.1.0", "repo2:latest",
|
|
"repo3:3.0.1", "repo3:3.1.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 10)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo3:latest",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
amdFilter := &gql_generated.Filter{Arch: []*string{&AMD}}
|
|
pageInput = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, metaDB, 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 = getGQLPageInput(2, 2)
|
|
|
|
images, err = getImageListForCVE(responseContext, "CVE3", cveInfo, amdFilter, pageInput, metaDB, 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)
|
|
}
|
|
})
|
|
})
|
|
|
|
Convey("Get a list of images where a particular CVE is fixed", t, func() {
|
|
Convey("Unpaginated request", func() {
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, nil, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages := []string{
|
|
"repo1:1.0.1", "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)
|
|
}
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, nil, metaDB, 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)
|
|
}
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE3", "repo1", cveInfo, nil, nil, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
})
|
|
|
|
Convey("paginated fail", func() {
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err = getImageListWithCVEFixed(responseContext, "cve", "repo1:1.1.0", cveInfo, &gql_generated.Filter{},
|
|
pageInput, mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.1.0": {
|
|
Digest: godigest.FromString("str").String(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
}, log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("context done", func() {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
cancel()
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, nil, metaDB, log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("Paginated requests", func() {
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover,
|
|
)
|
|
|
|
pageInput := getGQLPageInput(1, 0)
|
|
|
|
images, err := getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, 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)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(1, 1)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.1.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(1, 2)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, 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)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(1, 3)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(1, 10)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
So(len(images.Results), ShouldEqual, 0)
|
|
|
|
pageInput = getGQLPageInput(2, 0)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.1", "repo1:1.1.0",
|
|
}
|
|
So(len(images.Results), ShouldEqual, len(expectedImages))
|
|
|
|
for _, image := range images.Results {
|
|
So(fmt.Sprintf("%s:%s", *image.RepoName, *image.Tag), ShouldBeIn, expectedImages)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(2, 1)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, 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 = getGQLPageInput(2, 2)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, 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)
|
|
}
|
|
|
|
pageInput = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, nil, pageInput, metaDB, log)
|
|
So(err, ShouldBeNil)
|
|
|
|
expectedImages = []string{
|
|
"repo1:1.0.1", "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 = getGQLPageInput(5, 0)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, metaDB, 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 = getGQLPageInput(5, 2)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE2", "repo1", cveInfo, nil, pageInput, metaDB, 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 = getGQLPageInput(3, 0)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, amdFilter, pageInput, metaDB, 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, metaDB, 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 = getGQLPageInput(1, 1)
|
|
|
|
images, err = getImageListWithCVEFixed(responseContext, "CVE1", "repo1", cveInfo, armFilter, pageInput, metaDB, 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)
|
|
}
|
|
})
|
|
})
|
|
|
|
Convey("Errors for cve resolvers", t, func() {
|
|
_, err := getImageListForCVE(
|
|
ctx,
|
|
"id",
|
|
mocks.CveInfoMock{
|
|
GetImageListForCVEFn: func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) {
|
|
return []cvemodel.TagInfo{}, ErrTestError
|
|
},
|
|
},
|
|
nil,
|
|
nil,
|
|
mocks.MetaDBMock{
|
|
GetMultipleRepoMetaFn: func(ctx context.Context, filter func(repoMeta mTypes.RepoMeta) bool,
|
|
) ([]mTypes.RepoMeta, error) {
|
|
return []mTypes.RepoMeta{{}}, nil
|
|
},
|
|
},
|
|
log,
|
|
)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CVE Diff between images", t, func() {
|
|
// image := "image:tag"
|
|
// baseImage := "base:basetag"
|
|
ctx := context.Background()
|
|
pageInput := &gql_generated.PageInput{
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
boltDriver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: t.TempDir()})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
metaDB, err := boltdb.New(boltDriver, log)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
layer1 := []byte{10, 20, 30}
|
|
layer2 := []byte{11, 21, 31}
|
|
layer3 := []byte{12, 22, 23}
|
|
|
|
otherImage := CreateImageWith().LayerBlobs([][]byte{
|
|
layer1,
|
|
}).DefaultConfig().Build()
|
|
|
|
baseImage := CreateImageWith().LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
}).PlatformConfig("testArch", "testOs").Build()
|
|
|
|
image := CreateImageWith().LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
}).PlatformConfig("testArch", "testOs").Build()
|
|
|
|
multiArchBase := CreateMultiarchWith().Images([]Image{baseImage, CreateRandomImage(), CreateRandomImage()}).
|
|
Build()
|
|
multiArchImage := CreateMultiarchWith().Images([]Image{image, CreateRandomImage(), CreateRandomImage()}).
|
|
Build()
|
|
|
|
getCveResults := func(digestStr string) map[string]cvemodel.CVE {
|
|
switch digestStr {
|
|
case image.DigestStr():
|
|
return map[string]cvemodel.CVE{
|
|
"CVE1": {
|
|
ID: "CVE1",
|
|
Severity: "HIGH",
|
|
Title: "Title CVE1",
|
|
Description: "Description CVE1",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
"CVE2": {
|
|
ID: "CVE2",
|
|
Severity: "MEDIUM",
|
|
Title: "Title CVE2",
|
|
Description: "Description CVE2",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
"CVE3": {
|
|
ID: "CVE3",
|
|
Severity: "LOW",
|
|
Title: "Title CVE3",
|
|
Description: "Description CVE3",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
}
|
|
case baseImage.DigestStr():
|
|
return map[string]cvemodel.CVE{
|
|
"CVE1": {
|
|
ID: "CVE1",
|
|
Severity: "HIGH",
|
|
Title: "Title CVE1",
|
|
Description: "Description CVE1",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
"CVE2": {
|
|
ID: "CVE2",
|
|
Severity: "MEDIUM",
|
|
Title: "Title CVE2",
|
|
Description: "Description CVE2",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
}
|
|
case otherImage.DigestStr():
|
|
return map[string]cvemodel.CVE{
|
|
"CVE1": {
|
|
ID: "CVE1",
|
|
Severity: "HIGH",
|
|
Title: "Title CVE1",
|
|
Description: "Description CVE1",
|
|
PackageList: []cvemodel.Package{{}},
|
|
},
|
|
}
|
|
}
|
|
|
|
// By default the image has no vulnerabilities
|
|
return map[string]cvemodel.CVE{}
|
|
}
|
|
|
|
// MetaDB loaded with initial data, now mock the scanner
|
|
// Setup test CVE data in mock scanner
|
|
scanner := mocks.CveScannerMock{
|
|
ScanImageFn: func(ctx context.Context, image string) (map[string]cvemodel.CVE, error) {
|
|
repo, ref, _, _ := common.GetRepoReference(image)
|
|
|
|
if common.IsDigest(ref) {
|
|
return getCveResults(ref), nil
|
|
}
|
|
|
|
repoMeta, _ := metaDB.GetRepoMeta(ctx, repo)
|
|
|
|
if _, ok := repoMeta.Tags[ref]; !ok {
|
|
panic("unexpected tag '" + ref + "', test might be wrong")
|
|
}
|
|
|
|
return getCveResults(repoMeta.Tags[ref].Digest), nil
|
|
},
|
|
GetCachedResultFn: func(digestStr string) map[string]cvemodel.CVE {
|
|
return getCveResults(digestStr)
|
|
},
|
|
IsResultCachedFn: func(digestStr string) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
cveInfo := &cveinfo.BaseCveInfo{
|
|
Log: log,
|
|
Scanner: scanner,
|
|
MetaDB: metaDB,
|
|
}
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB,
|
|
ociutils.Repo{
|
|
Name: "repo",
|
|
Images: []ociutils.RepoImage{
|
|
{Image: otherImage, Reference: "other-image"},
|
|
{Image: baseImage, Reference: "base-image"},
|
|
{Image: image, Reference: "image"},
|
|
},
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo-multi",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: CreateRandomMultiarch(), Reference: "multi-rand"},
|
|
{MultiarchImage: multiArchBase, Reference: "multi-base"},
|
|
{MultiarchImage: multiArchImage, Reference: "multi-img"},
|
|
},
|
|
},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
|
|
minuend := gql_generated.ImageInput{Repo: "repo", Tag: "image"}
|
|
subtrahend := gql_generated.ImageInput{Repo: "repo", Tag: "image"}
|
|
diffResult, err := getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(len(diffResult.CVEList), ShouldEqual, 0)
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo", Tag: "image"}
|
|
subtrahend = gql_generated.ImageInput{}
|
|
diffResult, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(len(diffResult.CVEList), ShouldEqual, 1)
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo", Tag: "base-image"}
|
|
subtrahend = gql_generated.ImageInput{Repo: "repo", Tag: "image"}
|
|
diffResult, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(len(diffResult.CVEList), ShouldEqual, 0)
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-img", Platform: &gql_generated.PlatformInput{}}
|
|
subtrahend = gql_generated.ImageInput{}
|
|
_, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-img", Platform: &gql_generated.PlatformInput{
|
|
Os: ref("testOs"),
|
|
Arch: ref("testArch"),
|
|
}}
|
|
subtrahend = gql_generated.ImageInput{}
|
|
diffResult, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(len(diffResult.CVEList), ShouldEqual, 1)
|
|
So(diffResult.Subtrahend.Repo, ShouldEqual, "repo-multi")
|
|
So(diffResult.Subtrahend.Tag, ShouldEqual, "multi-base")
|
|
So(dderef(diffResult.Subtrahend.Platform.Os), ShouldResemble, "testOs")
|
|
So(dderef(diffResult.Subtrahend.Platform.Arch), ShouldResemble, "testArch")
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-img", Platform: &gql_generated.PlatformInput{
|
|
Os: ref("testOs"),
|
|
Arch: ref("testArch"),
|
|
}}
|
|
subtrahend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-base", Platform: &gql_generated.PlatformInput{
|
|
Os: ref("testOs"),
|
|
Arch: ref("testArch"),
|
|
}}
|
|
diffResult, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldBeNil)
|
|
So(len(diffResult.CVEList), ShouldEqual, 1)
|
|
|
|
minuend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-img", Platform: &gql_generated.PlatformInput{
|
|
Os: ref("testOs"),
|
|
Arch: ref("testArch"),
|
|
}}
|
|
subtrahend = gql_generated.ImageInput{Repo: "repo-multi", Tag: "multi-base", Platform: &gql_generated.PlatformInput{}}
|
|
_, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, pageInput, "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CVE Diff Errors", t, func() {
|
|
ctx := context.Background()
|
|
metaDB := mocks.MetaDBMock{}
|
|
cveInfo := mocks.CveInfoMock{}
|
|
emptyImage := gql_generated.ImageInput{}
|
|
|
|
Convey("minuend is empty", func() {
|
|
_, err := getCVEDiffListForImages(ctx, emptyImage, emptyImage, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("no ", func() {
|
|
minuend := gql_generated.ImageInput{Repo: "repo", Tag: "bad-tag"}
|
|
_, err := getCVEDiffListForImages(ctx, minuend, emptyImage, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("getImageSummary for subtrahend errors", func() {
|
|
metaDB.GetRepoMetaFn = func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, ErrTestError
|
|
}
|
|
minuend := gql_generated.ImageInput{Repo: "test", Tag: "img"}
|
|
_, err := getCVEDiffListForImages(ctx, minuend, emptyImage, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
|
|
metaDB.GetRepoMetaFn = func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, zerr.ErrRepoMetaNotFound
|
|
}
|
|
minuend = gql_generated.ImageInput{Repo: "test", Tag: "img"}
|
|
_, err = getCVEDiffListForImages(ctx, minuend, emptyImage, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("FilterTags for subtrahend errors", func() {
|
|
metaDB.FilterTagsFn = func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return nil, ErrTestError
|
|
}
|
|
minuend := gql_generated.ImageInput{Repo: "test", Tag: "img"}
|
|
_, err = getCVEDiffListForImages(ctx, minuend, emptyImage, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetCVEDiffListForImages errors", func() {
|
|
cveInfo.GetCVEDiffListForImagesFn = func(ctx context.Context, minuend, subtrahend, searchedCVE, excluded string,
|
|
pageInput cvemodel.PageInput,
|
|
) ([]cvemodel.CVE, cvemodel.ImageCVESummary, common.PageInfo, error) {
|
|
return nil, cvemodel.ImageCVESummary{}, common.PageInfo{}, ErrTestError
|
|
}
|
|
minuend := gql_generated.ImageInput{Repo: "test", Tag: "img"}
|
|
subtrahend := gql_generated.ImageInput{Repo: "sub", Tag: "img"}
|
|
_, err = getCVEDiffListForImages(ctx, minuend, subtrahend, metaDB, cveInfo, getGQLPageInput(0, 0), "", "", log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestMockedDerivedImageList(t *testing.T) {
|
|
Convey("MetaDB FilterTags error", t, func() {
|
|
log := log.NewLogger("debug", "/dev/null")
|
|
|
|
image := CreateRandomImage()
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(images.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("paginated fail", t, func() {
|
|
log := log.NewLogger("debug", "/dev/null")
|
|
image := CreateRandomImage()
|
|
pageInput := &gql_generated.PageInput{
|
|
Limit: ref(-1),
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := derivedImageList(responseContext, "repo1:1.0.1", nil,
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {
|
|
Digest: image.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
|
|
},
|
|
},
|
|
pageInput,
|
|
mocks.CveInfoMock{}, log)
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
//nolint: dupl
|
|
Convey("MetaDB FilterTags no repo available", t, func() {
|
|
log := log.NewLogger("debug", "/dev/null")
|
|
image := CreateDefaultImage()
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, nil
|
|
},
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{
|
|
digests[0]: image.AsImageMeta(),
|
|
}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return image.AsImageMeta(), nil
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log)
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
//nolint: dupl
|
|
Convey("derived image list working", t, func() {
|
|
log := log.NewLogger("debug", "/dev/null")
|
|
layer1 := []byte{10, 11, 10, 11}
|
|
layer2 := []byte{11, 11, 11, 11}
|
|
layer3 := []byte{10, 10, 10, 11}
|
|
layer4 := []byte{13, 14, 15, 11}
|
|
|
|
image := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
}).DefaultConfig().Build()
|
|
|
|
derivedImage := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
layer4,
|
|
}).DefaultConfig().Build()
|
|
|
|
imageMetaMap := map[string]mTypes.ImageMeta{
|
|
image.DigestStr(): image.AsImageMeta(),
|
|
derivedImage.DigestStr(): derivedImage.AsImageMeta(),
|
|
}
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return imageMetaMap[digest.String()], nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
result := map[string]mTypes.ImageMeta{}
|
|
|
|
for _, digest := range digests {
|
|
result[digest] = imageMetaMap[digest]
|
|
}
|
|
|
|
return result, nil
|
|
},
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
fullImageMetaList := []mTypes.FullImageMeta{}
|
|
repos := []mTypes.RepoMeta{{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
"1.0.3": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}}
|
|
|
|
for _, repo := range repos {
|
|
for tag, descriptor := range repo.Tags {
|
|
if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
|
|
fullImageMetaList = append(fullImageMetaList,
|
|
convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
|
|
}
|
|
}
|
|
}
|
|
|
|
return fullImageMetaList, nil
|
|
},
|
|
}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
|
|
Convey("valid derivedImageList, results not affected by pageInput", func() {
|
|
images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log)
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldNotBeEmpty)
|
|
So(len(images.Results), ShouldEqual, 2)
|
|
})
|
|
|
|
Convey("valid derivedImageList, results affected by pageInput", func() {
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: ref(1),
|
|
Offset: ref(0),
|
|
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
|
|
}
|
|
|
|
images, err := derivedImageList(responseContext, "repo1:1.0.1", nil, mockMetaDB, &pageInput,
|
|
mockCve, log)
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldNotBeEmpty)
|
|
So(len(images.Results), ShouldEqual, 1)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestMockedBaseImageList(t *testing.T) {
|
|
Convey("MetaDB FilterTags error", t, func() {
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, ErrTestError
|
|
},
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{}, ErrTestError
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{}, ErrTestError
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
So(images.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
Convey("paginated fail", t, func() {
|
|
image := CreateDefaultImage()
|
|
pageInput := &gql_generated.PageInput{Limit: ref(-1)}
|
|
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
_, err := baseImageList(responseContext, "repo1:1.0.2", nil,
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.2": {
|
|
Digest: image.DigestStr(),
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
|
|
},
|
|
},
|
|
pageInput, mocks.CveInfoMock{}, log.NewLogger("debug", ""))
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
//nolint: dupl
|
|
Convey("MetaDB FilterTags no repo available", t, func() {
|
|
image := CreateDefaultImage()
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
return []mTypes.FullImageMeta{}, nil
|
|
},
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.2": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return map[string]mTypes.ImageMeta{image.DigestStr(): image.AsImageMeta()}, nil
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldBeEmpty)
|
|
})
|
|
|
|
//nolint: dupl
|
|
Convey("base image list working", t, func() {
|
|
layer1 := []byte{10, 11, 10, 11}
|
|
layer2 := []byte{11, 11, 11, 11}
|
|
layer3 := []byte{10, 10, 10, 11}
|
|
layer4 := []byte{13, 14, 15, 11}
|
|
|
|
image := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
}).DefaultConfig().Build()
|
|
|
|
derivedImage := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
layer4,
|
|
}).DefaultConfig().Build()
|
|
|
|
imageMetaMap := map[string]mTypes.ImageMeta{
|
|
image.DigestStr(): image.AsImageMeta(),
|
|
derivedImage.DigestStr(): derivedImage.AsImageMeta(),
|
|
}
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return imageMetaMap, nil
|
|
},
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
fullImageMetaList := []mTypes.FullImageMeta{}
|
|
repos := []mTypes.RepoMeta{{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
"1.0.3": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}}
|
|
|
|
for _, repo := range repos {
|
|
for tag, descriptor := range repo.Tags {
|
|
if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
|
|
fullImageMetaList = append(fullImageMetaList,
|
|
convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
|
|
}
|
|
}
|
|
}
|
|
|
|
return fullImageMetaList, nil
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
|
|
Convey("valid baseImageList, results not affected by pageInput", func() {
|
|
images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB,
|
|
&gql_generated.PageInput{}, mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldNotBeEmpty)
|
|
So(len(images.Results), ShouldEqual, 2)
|
|
expectedTags := []string{"1.0.1", "1.0.3"}
|
|
So(expectedTags, ShouldContain, *images.Results[0].Tag)
|
|
So(expectedTags, ShouldContain, *images.Results[1].Tag)
|
|
})
|
|
|
|
Convey("valid baseImageList, results affected by pageInput", func() {
|
|
limit := 1
|
|
offset := 0
|
|
sortCriteria := gql_generated.SortCriteriaAlphabeticAsc
|
|
pageInput := gql_generated.PageInput{
|
|
Limit: &limit,
|
|
Offset: &offset,
|
|
SortBy: &sortCriteria,
|
|
}
|
|
|
|
images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB,
|
|
&pageInput, mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldNotBeEmpty)
|
|
So(len(images.Results), ShouldEqual, limit)
|
|
So(*images.Results[0].Tag, ShouldEqual, "1.0.1")
|
|
})
|
|
})
|
|
|
|
//nolint: dupl
|
|
Convey("filterTags working, no base image list found", t, func() {
|
|
layer1 := []byte{10, 11, 10, 11}
|
|
layer2 := []byte{11, 11, 11, 11}
|
|
layer3 := []byte{10, 10, 10, 11}
|
|
layer4 := []byte{13, 14, 15, 11}
|
|
|
|
image := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer1,
|
|
layer2,
|
|
layer3,
|
|
}).DefaultConfig().Build()
|
|
|
|
derivedImage := CreateImageWith().
|
|
LayerBlobs([][]byte{
|
|
layer4,
|
|
}).DefaultConfig().Build()
|
|
|
|
imageMetaMap := map[string]mTypes.ImageMeta{
|
|
image.DigestStr(): image.AsImageMeta(),
|
|
derivedImage.DigestStr(): derivedImage.AsImageMeta(),
|
|
}
|
|
|
|
mockMetaDB := mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}, nil
|
|
},
|
|
FilterImageMetaFn: func(ctx context.Context, digests []string) (map[string]mTypes.ImageMeta, error) {
|
|
return imageMetaMap, nil
|
|
},
|
|
FilterTagsFn: func(ctx context.Context, filterRepoTag mTypes.FilterRepoTagFunc, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.FullImageMeta, error) {
|
|
fullImageMetaList := []mTypes.FullImageMeta{}
|
|
repos := []mTypes.RepoMeta{{
|
|
Name: "repo1",
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{
|
|
"1.0.1": {Digest: image.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
"1.0.2": {Digest: derivedImage.DigestStr(), MediaType: ispec.MediaTypeImageManifest},
|
|
},
|
|
}}
|
|
|
|
for _, repo := range repos {
|
|
for tag, descriptor := range repo.Tags {
|
|
if filterFunc(repo, imageMetaMap[descriptor.Digest]) {
|
|
fullImageMetaList = append(fullImageMetaList,
|
|
convert.GetFullImageMeta(tag, repo, imageMetaMap[descriptor.Digest]))
|
|
}
|
|
}
|
|
}
|
|
|
|
return fullImageMetaList, nil
|
|
},
|
|
}
|
|
responseContext := graphql.WithResponseContext(context.Background(), graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
mockCve := mocks.CveInfoMock{}
|
|
images, err := baseImageList(responseContext, "repo1:1.0.2", nil, mockMetaDB, &gql_generated.PageInput{},
|
|
mockCve, log.NewLogger("debug", ""))
|
|
So(err, ShouldBeNil)
|
|
So(images.Results, ShouldBeEmpty)
|
|
})
|
|
}
|
|
|
|
func TestExpandedRepoInfoErrors(t *testing.T) {
|
|
log := log.NewLogger("debug", "")
|
|
|
|
Convey("Access error", t, func() {
|
|
userAc := reqCtx.NewUserAccessControl()
|
|
userAc.SetUsername("user")
|
|
userAc.SetGlobPatterns("read", map[string]bool{
|
|
"repo": false,
|
|
})
|
|
|
|
ctx := userAc.DeriveContext(context.Background())
|
|
|
|
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
|
|
graphql.DefaultRecover)
|
|
|
|
_, err := expandedRepoInfo(responseContext, "repo", mocks.MetaDBMock{}, mocks.CveInfoMock{}, log)
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestUtils(t *testing.T) {
|
|
Convey("utils", t, func() {
|
|
Convey("", func() {
|
|
So(isMatchingPlatform(ispec.Platform{OS: "test"}, gql_generated.PlatformInput{Os: ref("t")}), ShouldBeFalse)
|
|
So(getArch(ispec.Platform{OS: "t", Architecture: "e", Variant: "st"}), ShouldResemble, "e/st")
|
|
})
|
|
|
|
Convey("checkImageInput", func() {
|
|
_, err := resolveImageData(context.Background(), gql_generated.ImageInput{Repo: "test"}, mocks.MetaDBMock{})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("checkImageInput can't find index data", func() {
|
|
_, err := resolveImageData(context.Background(), gql_generated.ImageInput{
|
|
Repo: "test", Tag: "test", Digest: ref("dig"),
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{Tags: map[string]mTypes.Descriptor{
|
|
"test": {MediaType: ispec.MediaTypeImageIndex},
|
|
}}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return mTypes.ImageMeta{}, ErrTestError
|
|
},
|
|
})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("checkImageInput image meta not found", func() {
|
|
_, err := resolveImageData(context.Background(), gql_generated.ImageInput{
|
|
Repo: "test", Tag: "test", Digest: ref("dig"),
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{Tags: map[string]mTypes.Descriptor{
|
|
"test": {MediaType: ispec.MediaTypeImageIndex},
|
|
}}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return mTypes.ImageMeta{}, nil
|
|
},
|
|
})
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
Convey("checkImageInput image meta bad media type", func() {
|
|
_, err := resolveImageData(context.Background(), gql_generated.ImageInput{
|
|
Repo: "test", Tag: "test", Digest: ref("dig"),
|
|
},
|
|
mocks.MetaDBMock{
|
|
GetRepoMetaFn: func(ctx context.Context, repo string) (mTypes.RepoMeta, error) {
|
|
return mTypes.RepoMeta{Tags: map[string]mTypes.Descriptor{
|
|
"test": {MediaType: "bad-type"},
|
|
}}, nil
|
|
},
|
|
GetImageMetaFn: func(digest godigest.Digest) (mTypes.ImageMeta, error) {
|
|
return mTypes.ImageMeta{}, nil
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
})
|
|
})
|
|
}
|
|
|
|
func getGQLPageInput(limit int, offset int) *gql_generated.PageInput {
|
|
sortCriteria := gql_generated.SortCriteriaAlphabeticAsc
|
|
|
|
return &gql_generated.PageInput{
|
|
Limit: &limit,
|
|
Offset: &offset,
|
|
SortBy: &sortCriteria,
|
|
}
|
|
}
|