mirror of
https://github.com/project-zot/zot.git
synced 2025-02-24 23:56:32 -05:00
Which could be imported independently. See more details: 1. "zotregistry.io/zot/pkg/test/common" - currently used as tcommon "zotregistry.io/zot/pkg/test/common" - inside pkg/test test "zotregistry.io/zot/pkg/test/common" - in tests . "zotregistry.io/zot/pkg/test/common" - in tests Decouple zb from code in test/pkg in order to keep the size small. 2. "zotregistry.io/zot/pkg/test/image-utils" - curently used as . "zotregistry.io/zot/pkg/test/image-utils" 3. "zotregistry.io/zot/pkg/test/deprecated" - curently used as "zotregistry.io/zot/pkg/test/deprecated" This one will bre replaced gradually by image-utils in the future. 4. "zotregistry.io/zot/pkg/test/signature" - (cosign + notation) use as "zotregistry.io/zot/pkg/test/signature" 5. "zotregistry.io/zot/pkg/test/auth" - (bearer + oidc) curently used as authutils "zotregistry.io/zot/pkg/test/auth" 6. "zotregistry.io/zot/pkg/test/oci-utils" - curently used as ociutils "zotregistry.io/zot/pkg/test/oci-utils" Some unused functions were removed, some were replaced, and in a few cases specific funtions were moved to the files they were used in. Added an interface for the StoreController, this reduces the number of imports of the entire image store, decreasing binary size for tests. If the zb code was still coupled with pkg/test, this would have reflected in zb size. Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
452 lines
13 KiB
Go
452 lines
13 KiB
Go
//go:build sync && scrub && metrics && search
|
|
// +build sync,scrub,metrics,search
|
|
|
|
package ociutils_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
"zotregistry.io/zot/pkg/api"
|
|
"zotregistry.io/zot/pkg/api/config"
|
|
extconf "zotregistry.io/zot/pkg/extensions/config"
|
|
"zotregistry.io/zot/pkg/extensions/monitoring"
|
|
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
|
|
"zotregistry.io/zot/pkg/log"
|
|
"zotregistry.io/zot/pkg/storage"
|
|
"zotregistry.io/zot/pkg/storage/local"
|
|
tcommon "zotregistry.io/zot/pkg/test/common"
|
|
. "zotregistry.io/zot/pkg/test/image-utils"
|
|
"zotregistry.io/zot/pkg/test/mocks"
|
|
ociutils "zotregistry.io/zot/pkg/test/oci-utils"
|
|
signature "zotregistry.io/zot/pkg/test/signature"
|
|
)
|
|
|
|
var ErrTestError = fmt.Errorf("testError")
|
|
|
|
func TestBaseOciLayoutUtils(t *testing.T) {
|
|
Convey("GetImageManifestSize fail", t, func() {
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
return []byte{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
size := olu.GetImageManifestSize("", "")
|
|
So(size, ShouldBeZeroValue)
|
|
})
|
|
|
|
Convey("GetImageConfigSize: fail GetImageBlobManifest", t, func() {
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
return []byte{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
size := olu.GetImageConfigSize("", "")
|
|
So(size, ShouldBeZeroValue)
|
|
})
|
|
|
|
Convey("GetImageConfigSize: config GetBlobContent fail", t, func() {
|
|
image := CreateRandomImage()
|
|
manifestDigest := image.ConfigDescriptor.Digest.String()
|
|
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
if digest.String() == manifestDigest {
|
|
return []byte{}, ErrTestError
|
|
}
|
|
|
|
return []byte(
|
|
`
|
|
{
|
|
"schemaVersion": 2,
|
|
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
|
"config": {
|
|
"mediaType": "application/vnd.oci.image.config.v1+json",
|
|
"digest": manifestDigest,
|
|
"size": 1476
|
|
},
|
|
"layers": [
|
|
{
|
|
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
|
"digest": "` + image.Manifest.Layers[0].Digest.String() + `",
|
|
"size": 76097157
|
|
}
|
|
]
|
|
}`), nil
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
size := olu.GetImageConfigSize("", "")
|
|
So(size, ShouldBeZeroValue)
|
|
})
|
|
|
|
Convey("GetRepoLastUpdated: config GetBlobContent fail", t, func() {
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return []byte{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err := olu.GetRepoLastUpdated("")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetImageTagsWithTimestamp: GetImageBlobManifest fails", t, func() {
|
|
index := ispec.Index{
|
|
Manifests: []ispec.Descriptor{
|
|
{Annotations: map[string]string{ispec.AnnotationRefName: "w"}}, {},
|
|
},
|
|
}
|
|
|
|
indexBlob, err := json.Marshal(index)
|
|
So(err, ShouldBeNil)
|
|
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return indexBlob, nil
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err = olu.GetImageTagsWithTimestamp("rep")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetImageTagsWithTimestamp: GetImageInfo fails", t, func() {
|
|
index := ispec.Index{
|
|
Manifests: []ispec.Descriptor{
|
|
{Annotations: map[string]string{ispec.AnnotationRefName: "w"}}, {},
|
|
},
|
|
}
|
|
|
|
indexBlob, err := json.Marshal(index)
|
|
So(err, ShouldBeNil)
|
|
|
|
manifest := ispec.Manifest{
|
|
Config: ispec.Descriptor{
|
|
MediaType: "application/vnd.oci.image.config.v1+json",
|
|
Digest: "configDigest",
|
|
},
|
|
Layers: []ispec.Descriptor{
|
|
{},
|
|
{},
|
|
},
|
|
}
|
|
|
|
manifestBlob, err := json.Marshal(manifest)
|
|
So(err, ShouldBeNil)
|
|
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
if digest.String() == "configDigest" {
|
|
return nil, ErrTestError
|
|
}
|
|
|
|
return manifestBlob, nil
|
|
},
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return indexBlob, nil
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err = olu.GetImageTagsWithTimestamp("repo")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("GetExpandedRepoInfo: fails", t, func() {
|
|
index := ispec.Index{
|
|
Manifests: []ispec.Descriptor{
|
|
{},
|
|
{
|
|
Annotations: map[string]string{
|
|
ispec.AnnotationRefName: "w",
|
|
ispec.AnnotationVendor: "vend",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
indexBlob, err := json.Marshal(index)
|
|
So(err, ShouldBeNil)
|
|
|
|
manifest := ispec.Manifest{
|
|
Annotations: map[string]string{
|
|
ispec.AnnotationRefName: "w",
|
|
ispec.AnnotationVendor: "vend",
|
|
},
|
|
Layers: []ispec.Descriptor{
|
|
{},
|
|
{},
|
|
},
|
|
}
|
|
|
|
manifestBlob, err := json.Marshal(manifest)
|
|
So(err, ShouldBeNil)
|
|
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return nil, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err = olu.GetExpandedRepoInfo("rep")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
// GetRepoLastUpdated fails
|
|
mockStoreController = mocks.MockedImageStore{
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return indexBlob, nil
|
|
},
|
|
}
|
|
|
|
storeController = storage.StoreController{DefaultStore: mockStoreController}
|
|
olu = ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err = olu.GetExpandedRepoInfo("rep")
|
|
So(err, ShouldNotBeNil)
|
|
|
|
// anotations
|
|
|
|
mockStoreController = mocks.MockedImageStore{
|
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
|
return indexBlob, nil
|
|
},
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
return manifestBlob, nil
|
|
},
|
|
}
|
|
|
|
storeController = storage.StoreController{DefaultStore: mockStoreController}
|
|
olu = ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err = olu.GetExpandedRepoInfo("rep")
|
|
So(err, ShouldBeNil)
|
|
})
|
|
|
|
Convey("GetImageInfo fail", t, func() {
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
|
return []byte{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
_, err := olu.GetImageInfo("", "")
|
|
So(err, ShouldNotBeNil)
|
|
})
|
|
|
|
Convey("CheckManifestSignature: notation", t, func() {
|
|
// GetReferrers - fails => checkNotarySignature returns false
|
|
mockStoreController := mocks.MockedImageStore{
|
|
GetImageManifestFn: func(name, reference string) ([]byte, godigest.Digest, string, error) {
|
|
return []byte{}, "", "", zerr.ErrRepoNotFound
|
|
},
|
|
GetReferrersFn: func(name string, digest godigest.Digest, mediaTypes []string) (ispec.Index, error) {
|
|
return ispec.Index{}, ErrTestError
|
|
},
|
|
}
|
|
|
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
|
|
|
check := olu.CheckManifestSignature("rep", godigest.FromString(""))
|
|
So(check, ShouldBeFalse)
|
|
|
|
// checkNotarySignature -> true
|
|
dir := t.TempDir()
|
|
|
|
port := tcommon.GetFreePort()
|
|
baseURL := tcommon.GetBaseURL(port)
|
|
conf := config.New()
|
|
conf.HTTP.Port = port
|
|
conf.Storage.RootDirectory = dir
|
|
defaultVal := true
|
|
conf.Extensions = &extconf.ExtensionConfig{
|
|
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
|
|
}
|
|
|
|
conf.Extensions.Search.CVE = nil
|
|
|
|
ctlr := api.NewController(conf)
|
|
|
|
ctlrManager := tcommon.NewControllerManager(ctlr)
|
|
ctlrManager.StartAndWait(port)
|
|
defer ctlrManager.StopServer()
|
|
|
|
// push test image to repo
|
|
image := CreateRandomImage()
|
|
|
|
repo := "repo"
|
|
tag := "1.0.1"
|
|
err := UploadImage(image, baseURL, repo, tag)
|
|
So(err, ShouldBeNil)
|
|
|
|
olu = ociutils.NewBaseOciLayoutUtils(ctlr.StoreController, log.NewLogger("debug", ""))
|
|
manifestList, err := olu.GetImageManifests(repo)
|
|
So(err, ShouldBeNil)
|
|
So(len(manifestList), ShouldEqual, 1)
|
|
|
|
isSigned := olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
|
So(isSigned, ShouldBeFalse)
|
|
|
|
err = signature.SignImageUsingNotary(fmt.Sprintf("%s:%s", repo, tag), port)
|
|
So(err, ShouldBeNil)
|
|
|
|
isSigned = olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
|
So(isSigned, ShouldBeTrue)
|
|
})
|
|
}
|
|
|
|
func TestExtractImageDetails(t *testing.T) {
|
|
Convey("extractImageDetails good workflow", t, func() {
|
|
dir := t.TempDir()
|
|
testLogger := log.NewLogger("debug", "")
|
|
imageStore := local.NewImageStore(dir, false, false,
|
|
testLogger, monitoring.NewMetricsServer(false, testLogger), nil, nil)
|
|
|
|
storeController := storage.StoreController{
|
|
DefaultStore: imageStore,
|
|
}
|
|
|
|
image := CreateRandomImage()
|
|
|
|
err := WriteImageToFileSystem(image, "zot-test", "latest", storeController)
|
|
So(err, ShouldBeNil)
|
|
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, testLogger)
|
|
resDigest, resManifest, resIspecImage, resErr := olu.ExtractImageDetails("zot-test", "latest", testLogger)
|
|
So(string(resDigest), ShouldEqual, image.ManifestDescriptor.Digest.String())
|
|
So(resManifest.Config.Digest.String(), ShouldEqual, image.ConfigDescriptor.Digest.String())
|
|
|
|
So(resIspecImage.Architecture, ShouldContainSubstring, "amd64")
|
|
So(resErr, ShouldBeNil)
|
|
})
|
|
|
|
Convey("extractImageDetails bad ispec.ImageManifest", t, func() {
|
|
dir := t.TempDir()
|
|
testLogger := log.NewLogger("debug", "")
|
|
imageStore := local.NewImageStore(dir, false, false,
|
|
testLogger, monitoring.NewMetricsServer(false, testLogger), nil, nil)
|
|
|
|
storeController := storage.StoreController{
|
|
DefaultStore: imageStore,
|
|
}
|
|
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, testLogger)
|
|
resDigest, resManifest, resIspecImage, resErr := olu.ExtractImageDetails("zot-test",
|
|
"latest", testLogger)
|
|
So(resErr, ShouldEqual, zerr.ErrRepoNotFound)
|
|
So(string(resDigest), ShouldEqual, "")
|
|
So(resManifest, ShouldBeNil)
|
|
|
|
So(resIspecImage, ShouldBeNil)
|
|
})
|
|
|
|
Convey("extractImageDetails bad imageConfig", t, func() {
|
|
dir := t.TempDir()
|
|
testLogger := log.NewLogger("debug", "")
|
|
imageStore := local.NewImageStore(dir, false, false,
|
|
testLogger, monitoring.NewMetricsServer(false, testLogger), nil, nil)
|
|
|
|
storeController := storage.StoreController{
|
|
DefaultStore: imageStore,
|
|
}
|
|
|
|
image := CreateRandomImage()
|
|
|
|
err := WriteImageToFileSystem(image, "zot-test", "latest", storeController)
|
|
So(err, ShouldBeNil)
|
|
|
|
err = os.Remove(path.Join(dir, "zot-test/blobs/sha256", image.ConfigDescriptor.Digest.Encoded()))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
olu := ociutils.NewBaseOciLayoutUtils(storeController, testLogger)
|
|
resDigest, resManifest, resIspecImage, resErr := olu.ExtractImageDetails("zot-test", "latest", testLogger)
|
|
So(resErr, ShouldEqual, zerr.ErrBlobNotFound)
|
|
So(string(resDigest), ShouldEqual, "")
|
|
So(resManifest, ShouldBeNil)
|
|
So(resIspecImage, ShouldBeNil)
|
|
})
|
|
}
|
|
|
|
func TestTagsInfo(t *testing.T) {
|
|
Convey("Test tags info", t, func() {
|
|
allTags := make([]cvemodel.TagInfo, 0)
|
|
|
|
firstTag := cvemodel.TagInfo{
|
|
Tag: "1.0.0",
|
|
Descriptor: cvemodel.Descriptor{
|
|
Digest: "sha256:eca04f027f414362596f2632746d8a178362170b9ac9af772011fedcc3877ebb",
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Timestamp: time.Now(),
|
|
}
|
|
secondTag := cvemodel.TagInfo{
|
|
Tag: "1.0.1",
|
|
Descriptor: cvemodel.Descriptor{
|
|
Digest: "sha256:eca04f027f414362596f2632746d8a179362170b9ac9af772011fedcc3877ebb",
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Timestamp: time.Now(),
|
|
}
|
|
thirdTag := cvemodel.TagInfo{
|
|
Tag: "1.0.2",
|
|
Descriptor: cvemodel.Descriptor{
|
|
Digest: "sha256:eca04f027f414362596f2632746d8a170362170b9ac9af772011fedcc3877ebb",
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Timestamp: time.Now(),
|
|
}
|
|
fourthTag := cvemodel.TagInfo{
|
|
Tag: "1.0.3",
|
|
Descriptor: cvemodel.Descriptor{
|
|
Digest: "sha256:eca04f027f414362596f2632746d8a171362170b9ac9af772011fedcc3877ebb",
|
|
MediaType: ispec.MediaTypeImageManifest,
|
|
},
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
allTags = append(allTags, firstTag, secondTag, thirdTag, fourthTag)
|
|
|
|
latestTag := ociutils.GetLatestTag(allTags)
|
|
So(latestTag.Tag, ShouldEqual, "1.0.3")
|
|
})
|
|
}
|