mirror of
https://github.com/project-zot/zot.git
synced 2025-01-06 22:40:28 -05:00
d0eb043be5
Fallback to Created field and the History entries in the image config only if the annotation "org.opencontainers.image.created" is not available closes #2210 Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
890 lines
29 KiB
Go
890 lines
29 KiB
Go
//go:build search
|
|
|
|
package convert_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.dev/zot/pkg/extensions/search/convert"
|
|
"zotregistry.dev/zot/pkg/extensions/search/gql_generated"
|
|
"zotregistry.dev/zot/pkg/extensions/search/pagination"
|
|
"zotregistry.dev/zot/pkg/log"
|
|
"zotregistry.dev/zot/pkg/meta/boltdb"
|
|
mTypes "zotregistry.dev/zot/pkg/meta/types"
|
|
reqCtx "zotregistry.dev/zot/pkg/requestcontext"
|
|
. "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 TestUpdateLastUpdatedTimestamp(t *testing.T) {
|
|
Convey("Image summary is the first image checked for the repo", t, func() {
|
|
before := time.Time{}
|
|
after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC)
|
|
img := convert.UpdateLastUpdatedTimestamp(
|
|
&before,
|
|
&gql_generated.ImageSummary{LastUpdated: &before},
|
|
&gql_generated.ImageSummary{LastUpdated: &after},
|
|
)
|
|
|
|
So(*img.LastUpdated, ShouldResemble, after)
|
|
})
|
|
|
|
Convey("Image summary is updated after the current latest image", t, func() {
|
|
before := time.Date(2022, time.April, 1, 11, 0, 0, 0, time.UTC)
|
|
after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC)
|
|
img := convert.UpdateLastUpdatedTimestamp(
|
|
&before,
|
|
&gql_generated.ImageSummary{LastUpdated: &before},
|
|
&gql_generated.ImageSummary{LastUpdated: &after},
|
|
)
|
|
|
|
So(*img.LastUpdated, ShouldResemble, after)
|
|
})
|
|
|
|
Convey("Image summary is updated before the current latest image", t, func() {
|
|
before := time.Date(2022, time.April, 1, 11, 0, 0, 0, time.UTC)
|
|
after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC)
|
|
img := convert.UpdateLastUpdatedTimestamp(
|
|
&after,
|
|
&gql_generated.ImageSummary{LastUpdated: &after},
|
|
&gql_generated.ImageSummary{LastUpdated: &before},
|
|
)
|
|
|
|
So(*img.LastUpdated, ShouldResemble, after)
|
|
})
|
|
}
|
|
|
|
func TestLabels(t *testing.T) {
|
|
Convey("Test labels", t, func() {
|
|
// Test various labels
|
|
labels := make(map[string]string)
|
|
|
|
created := convert.GetCreated(labels)
|
|
So(created, ShouldBeNil)
|
|
|
|
desc := convert.GetDescription(labels)
|
|
So(desc, ShouldEqual, "")
|
|
|
|
license := convert.GetLicenses(labels)
|
|
So(license, ShouldEqual, "")
|
|
|
|
vendor := convert.GetVendor(labels)
|
|
So(vendor, ShouldEqual, "")
|
|
|
|
categories := convert.GetCategories(labels)
|
|
So(categories, ShouldEqual, "")
|
|
|
|
expectedCreatedTime := time.Date(2010, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
labels[ispec.AnnotationCreated] = expectedCreatedTime.Format(time.RFC3339)
|
|
labels[ispec.AnnotationVendor] = "zot"
|
|
labels[ispec.AnnotationDescription] = "zot-desc"
|
|
labels[ispec.AnnotationLicenses] = "zot-license"
|
|
labels[convert.AnnotationLabels] = "zot-labels"
|
|
|
|
created = convert.GetCreated(labels)
|
|
So(*created, ShouldEqual, expectedCreatedTime)
|
|
|
|
desc = convert.GetDescription(labels)
|
|
So(desc, ShouldEqual, "zot-desc")
|
|
|
|
license = convert.GetLicenses(labels)
|
|
So(license, ShouldEqual, "zot-license")
|
|
|
|
vendor = convert.GetVendor(labels)
|
|
So(vendor, ShouldEqual, "zot")
|
|
|
|
categories = convert.GetCategories(labels)
|
|
So(categories, ShouldEqual, "zot-labels")
|
|
|
|
labels = make(map[string]string)
|
|
|
|
// Use diff key
|
|
labels[convert.LabelAnnotationCreated] = expectedCreatedTime.Format(time.RFC3339)
|
|
labels[convert.LabelAnnotationVendor] = "zot-vendor"
|
|
labels[convert.LabelAnnotationDescription] = "zot-label-desc"
|
|
labels[ispec.AnnotationLicenses] = "zot-label-license"
|
|
|
|
created = convert.GetCreated(labels)
|
|
So(*created, ShouldEqual, expectedCreatedTime)
|
|
|
|
desc = convert.GetDescription(labels)
|
|
So(desc, ShouldEqual, "zot-label-desc")
|
|
|
|
license = convert.GetLicenses(labels)
|
|
So(license, ShouldEqual, "zot-label-license")
|
|
|
|
vendor = convert.GetVendor(labels)
|
|
So(vendor, ShouldEqual, "zot-vendor")
|
|
|
|
labels = make(map[string]string)
|
|
|
|
// Handle conversion errors
|
|
labels[ispec.AnnotationCreated] = "asd"
|
|
|
|
created = convert.GetCreated(labels)
|
|
So(created, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestGetSignaturesInfo(t *testing.T) {
|
|
Convey("Test get signatures info - cosign", t, func() {
|
|
digest := godigest.FromString("dig")
|
|
signatures := map[string]mTypes.ManifestSignatures{
|
|
digest.String(): {
|
|
"cosign": []mTypes.SignatureInfo{
|
|
{
|
|
LayersInfo: []mTypes.LayerInfo{
|
|
{
|
|
LayerContent: []byte{},
|
|
LayerDigest: "",
|
|
SignatureKey: "",
|
|
Signer: "author",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
signaturesSummary := convert.GetSignaturesInfo(true, signatures[digest.String()])
|
|
So(signaturesSummary, ShouldNotBeEmpty)
|
|
So(*signaturesSummary[0].Author, ShouldEqual, "author")
|
|
So(*signaturesSummary[0].IsTrusted, ShouldEqual, true)
|
|
So(*signaturesSummary[0].Tool, ShouldEqual, "cosign")
|
|
})
|
|
|
|
Convey("Test get signatures info - notation", t, func() {
|
|
digest := godigest.FromString("dig")
|
|
signatures := map[string]mTypes.ManifestSignatures{
|
|
digest.String(): {
|
|
"notation": []mTypes.SignatureInfo{
|
|
{
|
|
LayersInfo: []mTypes.LayerInfo{
|
|
{
|
|
LayerContent: []byte{},
|
|
LayerDigest: "",
|
|
SignatureKey: "",
|
|
Signer: "author",
|
|
Date: time.Now().AddDate(0, 0, -1),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
signaturesSummary := convert.GetSignaturesInfo(true, signatures[digest.String()])
|
|
So(signaturesSummary, ShouldNotBeEmpty)
|
|
So(*signaturesSummary[0].Author, ShouldEqual, "author")
|
|
So(*signaturesSummary[0].IsTrusted, ShouldEqual, false)
|
|
So(*signaturesSummary[0].Tool, ShouldEqual, "notation")
|
|
})
|
|
}
|
|
|
|
func TestAcceptedByFilter(t *testing.T) {
|
|
Convey("Images", t, func() {
|
|
Convey("Os not found", func() {
|
|
found := convert.ImgSumAcceptedByFilter(
|
|
&gql_generated.ImageSummary{
|
|
Manifests: []*gql_generated.ManifestSummary{
|
|
{Platform: &gql_generated.Platform{Os: ref("os1")}},
|
|
{Platform: &gql_generated.Platform{Os: ref("os2")}},
|
|
},
|
|
},
|
|
mTypes.Filter{Os: []*string{ref("os3")}},
|
|
)
|
|
|
|
So(found, ShouldBeFalse)
|
|
})
|
|
|
|
Convey("Has to be signed ", func() {
|
|
found := convert.ImgSumAcceptedByFilter(
|
|
&gql_generated.ImageSummary{
|
|
Manifests: []*gql_generated.ManifestSummary{
|
|
{IsSigned: ref(false)},
|
|
},
|
|
IsSigned: ref(false),
|
|
},
|
|
mTypes.Filter{HasToBeSigned: ref(true)},
|
|
)
|
|
|
|
So(found, ShouldBeFalse)
|
|
})
|
|
})
|
|
|
|
Convey("Repos", t, func() {
|
|
Convey("Os not found", func() {
|
|
found := convert.RepoSumAcceptedByFilter(
|
|
&gql_generated.RepoSummary{
|
|
Platforms: []*gql_generated.Platform{
|
|
{Os: ref("os1")},
|
|
{Os: ref("os2")},
|
|
},
|
|
},
|
|
mTypes.Filter{Os: []*string{ref("os3")}},
|
|
)
|
|
|
|
So(found, ShouldBeFalse)
|
|
})
|
|
|
|
Convey("Arch not found", func() {
|
|
found := convert.RepoSumAcceptedByFilter(
|
|
&gql_generated.RepoSummary{
|
|
Platforms: []*gql_generated.Platform{
|
|
{Arch: ref("Arch")},
|
|
},
|
|
},
|
|
mTypes.Filter{Arch: []*string{ref("arch_not_found")}},
|
|
)
|
|
|
|
So(found, ShouldBeFalse)
|
|
})
|
|
|
|
Convey("Has to be signed ", func() {
|
|
found := convert.ImgSumAcceptedByFilter(
|
|
&gql_generated.ImageSummary{
|
|
Manifests: []*gql_generated.ManifestSummary{
|
|
{IsSigned: ref(false)},
|
|
},
|
|
IsSigned: ref(false),
|
|
},
|
|
mTypes.Filter{HasToBeSigned: ref(true)},
|
|
)
|
|
|
|
So(found, ShouldBeFalse)
|
|
})
|
|
})
|
|
}
|
|
|
|
func ref[T any](val T) *T {
|
|
ref := val
|
|
|
|
return &ref
|
|
}
|
|
|
|
func TestPaginatedConvert(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
driver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: tempDir})
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
|
|
metaDB, err := boltdb.New(driver, log.NewLogger("debug", ""))
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
|
|
var (
|
|
badBothImage = CreateImageWith().DefaultLayers().ImageConfig(
|
|
ispec.Image{Platform: ispec.Platform{OS: "bad-os", Architecture: "bad-arch"}}).Build()
|
|
badOsImage = CreateImageWith().DefaultLayers().ImageConfig(
|
|
ispec.Image{Platform: ispec.Platform{OS: "bad-os", Architecture: "good-arch"}}).Build()
|
|
badArchImage = CreateImageWith().DefaultLayers().ImageConfig(
|
|
ispec.Image{Platform: ispec.Platform{OS: "good-os", Architecture: "bad-arch"}}).Build()
|
|
goodImage = CreateImageWith().DefaultLayers().ImageConfig(
|
|
ispec.Image{Platform: ispec.Platform{OS: "good-os", Architecture: "good-arch"}}).Build()
|
|
|
|
randomImage1 = CreateRandomImage()
|
|
randomImage2 = CreateRandomImage()
|
|
signatureDigest = godigest.FromString("signature")
|
|
|
|
badMultiArch = CreateMultiarchWith().Images(
|
|
[]Image{badBothImage, badOsImage, badArchImage, randomImage1}).Build()
|
|
goodMultiArch = CreateMultiarchWith().Images(
|
|
[]Image{badOsImage, badArchImage, randomImage2, goodImage}).Build()
|
|
)
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB,
|
|
ociutils.Repo{
|
|
Name: "repo1-only-images",
|
|
Images: []ociutils.RepoImage{
|
|
{Image: goodImage, Reference: "goodImage"},
|
|
{Image: badOsImage, Reference: "badOsImage"},
|
|
{Image: badArchImage, Reference: "badArchImage"},
|
|
{Image: badBothImage, Reference: "badBothImage"},
|
|
},
|
|
IsBookmarked: true,
|
|
IsStarred: true,
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo2-only-bad-images",
|
|
Images: []ociutils.RepoImage{
|
|
{Image: randomImage1, Reference: "randomImage1"},
|
|
{Image: randomImage2, Reference: "randomImage2"},
|
|
{Image: badBothImage, Reference: "badBothImage"},
|
|
},
|
|
IsBookmarked: true,
|
|
IsStarred: true,
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo3-only-multiarch",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: badMultiArch, Reference: "badMultiArch"},
|
|
{MultiarchImage: goodMultiArch, Reference: "goodMultiArch"},
|
|
},
|
|
IsBookmarked: true,
|
|
IsStarred: true,
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo4-not-bookmarked-or-starred",
|
|
Images: []ociutils.RepoImage{
|
|
{Image: goodImage, Reference: "goodImage"},
|
|
},
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: goodMultiArch, Reference: "goodMultiArch"},
|
|
},
|
|
},
|
|
ociutils.Repo{
|
|
Name: "repo5-signed",
|
|
Images: []ociutils.RepoImage{
|
|
{Image: goodImage, Reference: "goodImage"}, // is fake signed by the image below
|
|
},
|
|
Signatures: map[string]mTypes.ManifestSignatures{
|
|
goodImage.DigestStr(): ociutils.GetFakeSignatureInfo(signatureDigest.String()),
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
|
|
skipCVE := convert.SkipQGLField{Vulnerabilities: true}
|
|
|
|
Convey("PaginatedRepoMeta2RepoSummaries filtering and sorting", t, func() {
|
|
// Test different combinations of the filter
|
|
repoMetaList, err := metaDB.FilterRepos(ctx, mTypes.AcceptAllRepoNames, mTypes.AcceptAllRepoMeta)
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err := metaDB.FilterImageMeta(ctx, mTypes.GetLatestImageDigests(repoMetaList))
|
|
So(err, ShouldBeNil)
|
|
|
|
reposSum, pageInfo, err := convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
IsBookmarked: ref(true),
|
|
IsStarred: ref(true),
|
|
},
|
|
pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 2)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo1-only-images")
|
|
So(*reposSum[1].Name, ShouldResemble, "repo3-only-multiarch")
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
IsBookmarked: ref(true),
|
|
IsStarred: ref(true),
|
|
HasToBeSigned: ref(true),
|
|
},
|
|
pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 0)
|
|
So(pageInfo.ItemCount, ShouldEqual, 0)
|
|
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{
|
|
HasToBeSigned: ref(true),
|
|
},
|
|
pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 1)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo5-signed")
|
|
So(pageInfo.ItemCount, ShouldEqual, 1)
|
|
|
|
// no filter
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{}, pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 5)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo1-only-images")
|
|
So(*reposSum[1].Name, ShouldResemble, "repo2-only-bad-images")
|
|
So(*reposSum[2].Name, ShouldResemble, "repo3-only-multiarch")
|
|
So(*reposSum[3].Name, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*reposSum[4].Name, ShouldResemble, "repo5-signed")
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
// no filter opposite sorting
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{}, pagination.PageInput{SortBy: pagination.AlphabeticDsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 5)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo5-signed")
|
|
So(*reposSum[1].Name, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*reposSum[2].Name, ShouldResemble, "repo3-only-multiarch")
|
|
So(*reposSum[3].Name, ShouldResemble, "repo2-only-bad-images")
|
|
So(*reposSum[4].Name, ShouldResemble, "repo1-only-images")
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
// add pagination
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
IsBookmarked: ref(true),
|
|
IsStarred: ref(true),
|
|
},
|
|
pagination.PageInput{Limit: 1, Offset: 0, SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 1)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo1-only-images")
|
|
So(pageInfo.ItemCount, ShouldEqual, 1)
|
|
So(pageInfo.TotalCount, ShouldEqual, 2)
|
|
|
|
reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries(
|
|
ctx, repoMetaList, imageMeta,
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
IsBookmarked: ref(true),
|
|
IsStarred: ref(true),
|
|
},
|
|
pagination.PageInput{Limit: 1, Offset: 1, SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE,
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(reposSum), ShouldEqual, 1)
|
|
So(*reposSum[0].Name, ShouldResemble, "repo3-only-multiarch")
|
|
So(pageInfo.ItemCount, ShouldEqual, 1)
|
|
So(pageInfo.TotalCount, ShouldEqual, 2)
|
|
})
|
|
|
|
Convey("PaginatedRepoMeta2ImageSummaries filtering and sorting", t, func() {
|
|
fullImageMetaList, err := metaDB.FilterTags(ctx, mTypes.AcceptAllRepoTag, mTypes.AcceptAllImageMeta)
|
|
So(err, ShouldBeNil)
|
|
|
|
imgSum, pageInfo, err := convert.PaginatedFullImageMeta2ImageSummaries(
|
|
ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{},
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
},
|
|
pagination.PageInput{SortBy: pagination.AlphabeticAsc},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(imgSum), ShouldEqual, 5)
|
|
So(*imgSum[0].RepoName, ShouldResemble, "repo1-only-images")
|
|
So(*imgSum[0].Tag, ShouldResemble, "goodImage")
|
|
So(*imgSum[1].RepoName, ShouldResemble, "repo3-only-multiarch")
|
|
So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch")
|
|
So(*imgSum[2].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*imgSum[2].Tag, ShouldResemble, "goodImage")
|
|
So(*imgSum[3].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*imgSum[3].Tag, ShouldResemble, "goodMultiArch")
|
|
So(*imgSum[4].RepoName, ShouldResemble, "repo5-signed")
|
|
So(*imgSum[4].Tag, ShouldResemble, "goodImage")
|
|
So(pageInfo.ItemCount, ShouldEqual, 5)
|
|
|
|
// add page of size 2
|
|
imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries(
|
|
ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{},
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
},
|
|
pagination.PageInput{Limit: 2, Offset: 0, SortBy: pagination.AlphabeticAsc},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(imgSum), ShouldEqual, 2)
|
|
So(*imgSum[0].RepoName, ShouldResemble, "repo1-only-images")
|
|
So(*imgSum[0].Tag, ShouldResemble, "goodImage")
|
|
So(*imgSum[1].RepoName, ShouldResemble, "repo3-only-multiarch")
|
|
So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch")
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
// next page
|
|
imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries(
|
|
ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{},
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
},
|
|
pagination.PageInput{Limit: 2, Offset: 2, SortBy: pagination.AlphabeticAsc},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(imgSum), ShouldEqual, 2)
|
|
So(*imgSum[0].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*imgSum[0].Tag, ShouldResemble, "goodImage")
|
|
So(*imgSum[1].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred")
|
|
So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch")
|
|
So(pageInfo.ItemCount, ShouldEqual, 2)
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
// last page
|
|
imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries(
|
|
ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{},
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
},
|
|
pagination.PageInput{Limit: 2, Offset: 4, SortBy: pagination.AlphabeticAsc},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(imgSum), ShouldEqual, 1)
|
|
So(*imgSum[0].RepoName, ShouldResemble, "repo5-signed")
|
|
So(*imgSum[0].Tag, ShouldResemble, "goodImage")
|
|
So(pageInfo.ItemCount, ShouldEqual, 1)
|
|
So(pageInfo.TotalCount, ShouldEqual, 5)
|
|
|
|
// has to be signed
|
|
imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries(
|
|
ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{},
|
|
mTypes.Filter{
|
|
Os: []*string{ref("good-os")},
|
|
Arch: []*string{ref("good-arch")},
|
|
HasToBeSigned: ref(true),
|
|
},
|
|
pagination.PageInput{SortBy: pagination.AlphabeticAsc},
|
|
)
|
|
So(err, ShouldBeNil)
|
|
So(len(imgSum), ShouldEqual, 1)
|
|
So(*imgSum[0].RepoName, ShouldResemble, "repo5-signed")
|
|
So(*imgSum[0].Tag, ShouldResemble, "goodImage")
|
|
So(pageInfo.ItemCount, ShouldEqual, 1)
|
|
})
|
|
}
|
|
|
|
func TestIndexAnnotations(t *testing.T) {
|
|
Convey("Test ImageIndex2ImageSummary annotations logic", t, func() {
|
|
ctx := context.Background()
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
driver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: tempDir})
|
|
if err != nil {
|
|
t.FailNow()
|
|
}
|
|
|
|
metaDB, err := boltdb.New(driver, log.NewLogger("debug", ""))
|
|
So(err, ShouldBeNil)
|
|
|
|
defaultCreatedTime := *DefaultTimeRef()
|
|
configCreatedTime := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
manifestCreatedTime := time.Date(2010, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
indexCreatedTime := time.Date(2011, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
configLabels := map[string]string{
|
|
ispec.AnnotationCreated: configCreatedTime.Format(time.RFC3339),
|
|
ispec.AnnotationDescription: "ConfigDescription",
|
|
ispec.AnnotationLicenses: "ConfigLicenses",
|
|
ispec.AnnotationVendor: "ConfigVendor",
|
|
ispec.AnnotationAuthors: "ConfigAuthors",
|
|
ispec.AnnotationTitle: "ConfigTitle",
|
|
ispec.AnnotationDocumentation: "ConfigDocumentation",
|
|
ispec.AnnotationSource: "ConfigSource",
|
|
}
|
|
|
|
manifestAnnotations := map[string]string{
|
|
ispec.AnnotationCreated: manifestCreatedTime.Format(time.RFC3339),
|
|
ispec.AnnotationDescription: "ManifestDescription",
|
|
ispec.AnnotationLicenses: "ManifestLicenses",
|
|
ispec.AnnotationVendor: "ManifestVendor",
|
|
ispec.AnnotationAuthors: "ManifestAuthors",
|
|
ispec.AnnotationTitle: "ManifestTitle",
|
|
ispec.AnnotationDocumentation: "ManifestDocumentation",
|
|
ispec.AnnotationSource: "ManifestSource",
|
|
}
|
|
|
|
indexAnnotations := map[string]string{
|
|
ispec.AnnotationCreated: indexCreatedTime.Format(time.RFC3339),
|
|
ispec.AnnotationDescription: "IndexDescription",
|
|
ispec.AnnotationLicenses: "IndexLicenses",
|
|
ispec.AnnotationVendor: "IndexVendor",
|
|
ispec.AnnotationAuthors: "IndexAuthors",
|
|
ispec.AnnotationTitle: "IndexTitle",
|
|
ispec.AnnotationDocumentation: "IndexDocumentation",
|
|
ispec.AnnotationSource: "IndexSource",
|
|
}
|
|
|
|
imageWithConfigAnnotations := CreateImageWith().DefaultLayers().
|
|
ImageConfig(ispec.Image{
|
|
Config: ispec.ImageConfig{
|
|
Labels: configLabels,
|
|
},
|
|
}).Build()
|
|
|
|
imageWithManifestAndConfigAnnotations := CreateImageWith().DefaultLayers().
|
|
ImageConfig(ispec.Image{
|
|
Config: ispec.ImageConfig{
|
|
Labels: configLabels,
|
|
},
|
|
}).Annotations(manifestAnnotations).Build()
|
|
|
|
// --------------------------------------------------------
|
|
indexWithAnnotations := CreateMultiarchWith().Images(
|
|
[]Image{imageWithManifestAndConfigAnnotations},
|
|
).Annotations(indexAnnotations).Build()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB,
|
|
ociutils.Repo{
|
|
Name: "repo",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: indexWithAnnotations, Reference: "tag"},
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
|
|
repoMeta, err := metaDB.GetRepoMeta(ctx, "repo")
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err := metaDB.FilterImageMeta(ctx, []string{indexWithAnnotations.DigestStr()})
|
|
So(err, ShouldBeNil)
|
|
|
|
imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta,
|
|
imageMeta[indexWithAnnotations.DigestStr()]))
|
|
So(err, ShouldBeNil)
|
|
So(*imageSummary.LastUpdated, ShouldEqual, indexCreatedTime)
|
|
So(*imageSummary.Description, ShouldResemble, "IndexDescription")
|
|
So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses")
|
|
So(*imageSummary.Title, ShouldResemble, "IndexTitle")
|
|
So(*imageSummary.Source, ShouldResemble, "IndexSource")
|
|
So(*imageSummary.Documentation, ShouldResemble, "IndexDocumentation")
|
|
So(*imageSummary.Vendor, ShouldResemble, "IndexVendor")
|
|
So(*imageSummary.Authors, ShouldResemble, "IndexAuthors")
|
|
|
|
err = metaDB.ResetDB()
|
|
So(err, ShouldBeNil)
|
|
// --------------------------------------------------------
|
|
indexWithManifestAndConfigAnnotations := CreateMultiarchWith().Images(
|
|
[]Image{imageWithManifestAndConfigAnnotations, CreateRandomImage(), CreateRandomImage()},
|
|
).Build()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{
|
|
Name: "repo",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: indexWithManifestAndConfigAnnotations, Reference: "tag"},
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
|
|
digest := indexWithManifestAndConfigAnnotations.DigestStr()
|
|
|
|
repoMeta, err = metaDB.GetRepoMeta(ctx, "repo")
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest})
|
|
So(err, ShouldBeNil)
|
|
|
|
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta,
|
|
imageMeta[digest]))
|
|
So(err, ShouldBeNil)
|
|
So(*imageSummary.LastUpdated, ShouldEqual, manifestCreatedTime)
|
|
So(*imageSummary.Description, ShouldResemble, "ManifestDescription")
|
|
So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses")
|
|
So(*imageSummary.Title, ShouldResemble, "ManifestTitle")
|
|
So(*imageSummary.Source, ShouldResemble, "ManifestSource")
|
|
So(*imageSummary.Documentation, ShouldResemble, "ManifestDocumentation")
|
|
So(*imageSummary.Vendor, ShouldResemble, "ManifestVendor")
|
|
So(*imageSummary.Authors, ShouldResemble, "ManifestAuthors")
|
|
|
|
err = metaDB.ResetDB()
|
|
So(err, ShouldBeNil)
|
|
// --------------------------------------------------------
|
|
indexWithConfigAnnotations := CreateMultiarchWith().Images(
|
|
[]Image{imageWithConfigAnnotations, CreateRandomImage(), CreateRandomImage()},
|
|
).Build()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{
|
|
Name: "repo",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: indexWithConfigAnnotations, Reference: "tag"},
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
|
|
digest = indexWithConfigAnnotations.DigestStr()
|
|
|
|
repoMeta, err = metaDB.GetRepoMeta(ctx, "repo")
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest})
|
|
So(err, ShouldBeNil)
|
|
|
|
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta,
|
|
imageMeta[digest]))
|
|
So(err, ShouldBeNil)
|
|
So(*imageSummary.LastUpdated, ShouldEqual, configCreatedTime)
|
|
So(*imageSummary.Description, ShouldResemble, "ConfigDescription")
|
|
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
|
|
So(*imageSummary.Title, ShouldResemble, "ConfigTitle")
|
|
So(*imageSummary.Source, ShouldResemble, "ConfigSource")
|
|
So(*imageSummary.Documentation, ShouldResemble, "ConfigDocumentation")
|
|
So(*imageSummary.Vendor, ShouldResemble, "ConfigVendor")
|
|
So(*imageSummary.Authors, ShouldResemble, "ConfigAuthors")
|
|
|
|
err = metaDB.ResetDB()
|
|
So(err, ShouldBeNil)
|
|
//--------------------------------------------------------
|
|
|
|
indexWithMixAnnotations := CreateMultiarchWith().Images(
|
|
[]Image{
|
|
CreateImageWith().DefaultLayers().ImageConfig(ispec.Image{
|
|
Created: &defaultCreatedTime,
|
|
Config: ispec.ImageConfig{
|
|
Labels: map[string]string{
|
|
ispec.AnnotationDescription: "ConfigDescription",
|
|
ispec.AnnotationLicenses: "ConfigLicenses",
|
|
},
|
|
},
|
|
}).Annotations(map[string]string{
|
|
ispec.AnnotationVendor: "ManifestVendor",
|
|
ispec.AnnotationAuthors: "ManifestAuthors",
|
|
}).Build(),
|
|
CreateRandomImage(),
|
|
CreateRandomImage(),
|
|
},
|
|
).Annotations(
|
|
map[string]string{
|
|
ispec.AnnotationCreated: indexCreatedTime.Format(time.RFC3339),
|
|
ispec.AnnotationTitle: "IndexTitle",
|
|
ispec.AnnotationDocumentation: "IndexDocumentation",
|
|
ispec.AnnotationSource: "IndexSource",
|
|
},
|
|
).Build()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{
|
|
Name: "repo",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: indexWithMixAnnotations, Reference: "tag"},
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
|
|
digest = indexWithMixAnnotations.DigestStr()
|
|
|
|
repoMeta, err = metaDB.GetRepoMeta(ctx, "repo")
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest})
|
|
So(err, ShouldBeNil)
|
|
|
|
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta,
|
|
imageMeta[digest]))
|
|
So(err, ShouldBeNil)
|
|
So(*imageSummary.LastUpdated, ShouldEqual, indexCreatedTime)
|
|
So(*imageSummary.Description, ShouldResemble, "ConfigDescription")
|
|
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
|
|
So(*imageSummary.Vendor, ShouldResemble, "ManifestVendor")
|
|
So(*imageSummary.Authors, ShouldResemble, "ManifestAuthors")
|
|
So(*imageSummary.Title, ShouldResemble, "IndexTitle")
|
|
So(*imageSummary.Documentation, ShouldResemble, "IndexDocumentation")
|
|
So(*imageSummary.Source, ShouldResemble, "IndexSource")
|
|
|
|
err = metaDB.ResetDB()
|
|
So(err, ShouldBeNil)
|
|
//--------------------------------------------------------
|
|
indexWithNoAnnotations := CreateMultiarchWith().Images(
|
|
[]Image{
|
|
CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build(),
|
|
CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build(),
|
|
},
|
|
).Build()
|
|
|
|
ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{
|
|
Name: "repo",
|
|
MultiArchImages: []ociutils.RepoMultiArchImage{
|
|
{MultiarchImage: indexWithNoAnnotations, Reference: "tag"},
|
|
},
|
|
})
|
|
So(err, ShouldBeNil)
|
|
|
|
digest = indexWithNoAnnotations.DigestStr()
|
|
|
|
repoMeta, err = metaDB.GetRepoMeta(ctx, "repo")
|
|
So(err, ShouldBeNil)
|
|
imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest})
|
|
So(err, ShouldBeNil)
|
|
|
|
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta,
|
|
imageMeta[digest]))
|
|
So(err, ShouldBeNil)
|
|
So(*imageSummary.LastUpdated, ShouldEqual, defaultCreatedTime)
|
|
So(*imageSummary.Description, ShouldBeBlank)
|
|
So(*imageSummary.Licenses, ShouldBeBlank)
|
|
So(*imageSummary.Vendor, ShouldBeBlank)
|
|
So(*imageSummary.Authors, ShouldBeBlank)
|
|
So(*imageSummary.Title, ShouldBeBlank)
|
|
So(*imageSummary.Documentation, ShouldBeBlank)
|
|
So(*imageSummary.Source, ShouldBeBlank)
|
|
|
|
err = metaDB.ResetDB()
|
|
So(err, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestConvertErrors(t *testing.T) {
|
|
ctx := context.Background()
|
|
log := log.NewLogger("debug", "")
|
|
|
|
Convey("Errors", t, func() {
|
|
Convey("RepoMeta2ExpandedRepoInfo", func() {
|
|
_, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx,
|
|
mTypes.RepoMeta{
|
|
Tags: map[mTypes.Tag]mTypes.Descriptor{"tag": {MediaType: "bad-type", Digest: "digest"}},
|
|
},
|
|
map[string]mTypes.ImageMeta{
|
|
"digest": {},
|
|
},
|
|
convert.SkipQGLField{}, nil,
|
|
log,
|
|
)
|
|
So(len(imgSums), ShouldEqual, 0)
|
|
})
|
|
|
|
Convey("RepoMeta2ExpandedRepoInfo - bad ctx value", func() {
|
|
uacKey := reqCtx.GetContextKey()
|
|
ctx := context.WithValue(ctx, uacKey, "bad context")
|
|
|
|
_, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx,
|
|
mTypes.RepoMeta{},
|
|
map[string]mTypes.ImageMeta{
|
|
"digest": {},
|
|
},
|
|
convert.SkipQGLField{}, nil,
|
|
log,
|
|
)
|
|
So(len(imgSums), ShouldEqual, 0)
|
|
})
|
|
|
|
Convey("RepoMeta2ExpandedRepoInfo - nil ctx value", func() {
|
|
uacKey := reqCtx.GetContextKey()
|
|
ctx := context.WithValue(ctx, uacKey, nil)
|
|
|
|
_, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx,
|
|
mTypes.RepoMeta{},
|
|
map[string]mTypes.ImageMeta{
|
|
"digest": {},
|
|
},
|
|
convert.SkipQGLField{}, nil,
|
|
log,
|
|
)
|
|
So(len(imgSums), ShouldEqual, 0)
|
|
})
|
|
})
|
|
}
|