0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/extensions/search/digest/digest_test.go
Petu Eusebiu 19003e8a71 Added new extension "sync"
Periodically poll registries and pull images according to sync's config
Added sync on demand, syncing when clients asks for an image which
zot doesn't have.

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
2021-10-21 10:32:46 -07:00

425 lines
11 KiB
Go

// nolint: gochecknoinits
package digestinfo_test
import (
"context"
"encoding/json"
"io"
"io/ioutil"
"os"
"path"
"testing"
"time"
"github.com/anuvu/zot/pkg/api"
"github.com/anuvu/zot/pkg/api/config"
extconf "github.com/anuvu/zot/pkg/extensions/config"
digestinfo "github.com/anuvu/zot/pkg/extensions/search/digest"
"github.com/anuvu/zot/pkg/log"
"github.com/anuvu/zot/pkg/storage"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/resty.v1"
)
// nolint:gochecknoglobals
var (
digestInfo *digestinfo.DigestInfo
rootDir string
subRootDir string
)
const (
BaseURL1 = "http://127.0.0.1:8085"
Port1 = "8085"
)
type ImgResponseForDigest struct {
ImgListForDigest ImgListForDigest `json:"data"`
Errors []ErrorGQL `json:"errors"`
}
type ImgListForDigest struct {
Images []ImgInfo `json:"ImageListForDigest"`
}
type ImgInfo struct {
Name string `json:"Name"`
Tags []string `json:"Tags"`
}
type ErrorGQL struct {
Message string `json:"message"`
Path []string `json:"path"`
}
func init() {
err := testSetup()
if err != nil {
panic(err)
}
}
func testSetup() error {
dir, err := ioutil.TempDir("", "digest_test")
if err != nil {
return err
}
subDir, err := ioutil.TempDir("", "sub_digest_test")
if err != nil {
return err
}
rootDir = dir
subRootDir = subDir
// Test images used/copied:
// IMAGE NAME TAG DIGEST CONFIG LAYERS SIZE
// zot-test 0.0.1 2bacca16 adf3bb6c 76MB
// 2d473b07 76MB
// zot-cve-test 0.0.1 63a795ca 8dd57e17 75MB
// 7a0437f0 75MB
err = os.Mkdir(subDir+"/a", 0700)
if err != nil {
return err
}
err = copyFiles("../../../../test/data", rootDir)
if err != nil {
return err
}
err = copyFiles("../../../../test/data", subDir+"/a/")
if err != nil {
return err
}
log := log.NewLogger("debug", "")
storeController := storage.StoreController{DefaultStore: storage.NewImageStore(rootDir, false, false, log)}
digestInfo = digestinfo.NewDigestInfo(storeController, log)
return nil
}
func copyFiles(sourceDir string, destDir string) error {
sourceMeta, err := os.Stat(sourceDir)
if err != nil {
return err
}
if err := os.MkdirAll(destDir, sourceMeta.Mode()); err != nil {
return err
}
files, err := ioutil.ReadDir(sourceDir)
if err != nil {
return err
}
for _, file := range files {
sourceFilePath := path.Join(sourceDir, file.Name())
destFilePath := path.Join(destDir, file.Name())
if file.IsDir() {
if err = copyFiles(sourceFilePath, destFilePath); err != nil {
return err
}
} else {
sourceFile, err := os.Open(sourceFilePath)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(destFilePath)
if err != nil {
return err
}
defer destFile.Close()
if _, err = io.Copy(destFile, sourceFile); err != nil {
return err
}
}
}
return nil
}
func TestDigestInfo(t *testing.T) {
Convey("Test image tag", t, func() {
// Search by manifest digest
imageTags, err := digestInfo.GetImageTagsByDigest("zot-cve-test", "63a795ca")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by config digest
imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", "adf3bb6c")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by layer digest
imageTags, err = digestInfo.GetImageTagsByDigest("zot-cve-test", "7a0437f0")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 1)
So(*imageTags[0], ShouldEqual, "0.0.1")
// Search by non-existent image
imageTags, err = digestInfo.GetImageTagsByDigest("zot-tes", "63a795ca")
So(err, ShouldNotBeNil)
So(len(imageTags), ShouldEqual, 0)
// Search by non-existent digest
imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", "111")
So(err, ShouldBeNil)
So(len(imageTags), ShouldEqual, 0)
})
}
func TestDigestSearchHTTP(t *testing.T) {
Convey("Test image search by digest scanning", t, func() {
conf := config.New()
conf.HTTP.Port = Port1
conf.Storage.RootDirectory = rootDir
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{Enable: true},
}
c := api.NewController(conf)
go func() {
// this blocks
if err := c.Run(); err != nil {
return
}
}()
// wait till ready
for {
_, err := resty.R().Get(BaseURL1)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
// shut down server
defer func() {
ctx := context.Background()
_ = c.Server.Shutdown(ctx)
}()
resp, err := resty.R().Get(BaseURL1 + "/v2/")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
// "sha" should match all digests in all images
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
var responseStruct ImgResponseForDigest
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 2)
So(len(responseStruct.ImgListForDigest.Images[0].Tags), ShouldEqual, 1)
So(len(responseStruct.ImgListForDigest.Images[0].Tags), ShouldEqual, 1)
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-test","Tags":["0.0.1"]}]}}
// "2bacca16" should match the manifest of 1 image
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"2bacca16\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Name, ShouldEqual, "zot-test")
So(len(responseStruct.ImgListForDigest.Images[0].Tags), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Tags[0], ShouldEqual, "0.0.1")
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-test","Tags":["0.0.1"]}]}}
// "adf3bb6c" should match the config of 1 image
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"adf3bb6c\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Name, ShouldEqual, "zot-test")
So(len(responseStruct.ImgListForDigest.Images[0].Tags), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Tags[0], ShouldEqual, "0.0.1")
// Call should return {"data":{"ImageListForDigest":[{"Name":"zot-cve-test","Tags":["0.0.1"]}]}}
// "7a0437f0" should match the layer of 1 image
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"7a0437f0\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Name, ShouldEqual, "zot-cve-test")
So(len(responseStruct.ImgListForDigest.Images[0].Tags), ShouldEqual, 1)
So(responseStruct.ImgListForDigest.Images[0].Tags[0], ShouldEqual, "0.0.1")
// Call should return {"data":{"ImageListForDigest":[]}}
// "1111111" should match 0 images
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"1111111\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 0)
// Call should return {"errors": [{....}]", data":null}}
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"1111111\"){Name%20Tag343s}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 422)
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 1)
})
}
func TestDigestSearchHTTPSubPaths(t *testing.T) {
Convey("Test image search by digest scanning using storage subpaths", t, func() {
conf := config.New()
conf.HTTP.Port = Port1
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{Enable: true},
}
c := api.NewController(conf)
globalDir, err := ioutil.TempDir("", "digest_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(globalDir)
c.Config.Storage.RootDirectory = globalDir
subPathMap := make(map[string]config.StorageConfig)
subPathMap["/a"] = config.StorageConfig{RootDirectory: subRootDir}
c.Config.Storage.SubPaths = subPathMap
go func() {
// this blocks
if err := c.Run(); err != nil {
return
}
}()
// wait till ready
for {
_, err := resty.R().Get(BaseURL1)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
// shut down server
defer func() {
ctx := context.Background()
_ = c.Server.Shutdown(ctx)
}()
resp, err := resty.R().Get(BaseURL1 + "/v2/")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query?query={ImageListForDigest(id:\"sha\"){Name%20Tags}}")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
var responseStruct ImgResponseForDigest
err = json.Unmarshal(resp.Body(), &responseStruct)
So(err, ShouldBeNil)
So(len(responseStruct.Errors), ShouldEqual, 0)
So(len(responseStruct.ImgListForDigest.Images), ShouldEqual, 2)
})
}
func TestDigestSearchDisabled(t *testing.T) {
Convey("Test disabling image search", t, func() {
dir, err := ioutil.TempDir("", "digest_test")
So(err, ShouldBeNil)
conf := config.New()
conf.HTTP.Port = Port1
conf.Storage.RootDirectory = dir
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{Enable: false},
}
c := api.NewController(conf)
go func() {
// this blocks
if err := c.Run(); err != nil {
return
}
}()
// wait till ready
for {
_, err := resty.R().Get(BaseURL1)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
// shut down server
defer func() {
ctx := context.Background()
_ = c.Server.Shutdown(ctx)
}()
resp, err := resty.R().Get(BaseURL1 + "/v2/")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 200)
resp, err = resty.R().Get(BaseURL1 + "/query")
So(resp, ShouldNotBeNil)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 404)
})
}