mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
storage: add s3 backend support (without GC and dedupe)
Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
committed by
Ramkumar Chinchani
parent
8e4d828867
commit
9c568c0ee2
@@ -0,0 +1,720 @@
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
//"strings"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/anuvu/zot/pkg/extensions/monitoring"
|
||||
"github.com/anuvu/zot/pkg/log"
|
||||
"github.com/anuvu/zot/pkg/storage"
|
||||
"github.com/anuvu/zot/pkg/storage/s3"
|
||||
guuid "github.com/gofrs/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
// Add s3 support
|
||||
storageDriver "github.com/docker/distribution/registry/storage/driver"
|
||||
"github.com/docker/distribution/registry/storage/driver/factory"
|
||||
_ "github.com/docker/distribution/registry/storage/driver/s3-aws"
|
||||
|
||||
"gopkg.in/resty.v1"
|
||||
)
|
||||
|
||||
// nolint: gochecknoglobals
|
||||
var (
|
||||
testImage = "test"
|
||||
fileWriterSize = 12
|
||||
fileInfoSize = 10
|
||||
errorText = "new s3 error"
|
||||
errS3 = errors.New(errorText)
|
||||
)
|
||||
|
||||
func cleanupStorage(store storageDriver.StorageDriver, name string) {
|
||||
_ = store.Delete(context.Background(), name)
|
||||
}
|
||||
|
||||
func skipIt(t *testing.T) {
|
||||
if os.Getenv("S3MOCK_ENDPOINT") == "" {
|
||||
t.Skip("Skipping testing without AWS S3 mock server")
|
||||
}
|
||||
}
|
||||
|
||||
func createMockStorage(rootDir string, store storageDriver.StorageDriver) storage.ImageStore {
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := s3.NewImageStore(rootDir, false, false, log, metrics, store)
|
||||
|
||||
return il
|
||||
}
|
||||
|
||||
func createObjectsStore(rootDir string) (storageDriver.StorageDriver, storage.ImageStore, error) {
|
||||
bucket := "zot-storage-test"
|
||||
endpoint := os.Getenv("S3MOCK_ENDPOINT")
|
||||
storageDriverParams := map[string]interface{}{
|
||||
"rootDir": rootDir,
|
||||
"name": "s3",
|
||||
"region": "us-east-2",
|
||||
"bucket": bucket,
|
||||
"regionendpoint": endpoint,
|
||||
"secure": false,
|
||||
"skipverify": false,
|
||||
}
|
||||
|
||||
storeName := fmt.Sprintf("%v", storageDriverParams["name"])
|
||||
|
||||
store, err := factory.Create(storeName, storageDriverParams)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create bucket if it doesn't exists
|
||||
_, err = resty.R().Put("http://" + endpoint + "/" + bucket)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := s3.NewImageStore(rootDir, false, false, log, metrics, store)
|
||||
|
||||
return store, il, err
|
||||
}
|
||||
|
||||
type FileInfoMock struct {
|
||||
isDirFn func() bool
|
||||
}
|
||||
|
||||
func (f *FileInfoMock) Path() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f *FileInfoMock) Size() int64 {
|
||||
return int64(fileInfoSize)
|
||||
}
|
||||
|
||||
func (f *FileInfoMock) ModTime() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
func (f *FileInfoMock) IsDir() bool {
|
||||
if f != nil && f.isDirFn != nil {
|
||||
return f.isDirFn()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type FileWriterMock struct {
|
||||
writeFn func([]byte) (int, error)
|
||||
cancelFn func() error
|
||||
commitFn func() error
|
||||
closeFn func() error
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Size() int64 {
|
||||
return int64(fileWriterSize)
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Cancel() error {
|
||||
if f != nil && f.cancelFn != nil {
|
||||
return f.cancelFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Commit() error {
|
||||
if f != nil && f.commitFn != nil {
|
||||
return f.commitFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Write(p []byte) (int, error) {
|
||||
if f != nil && f.writeFn != nil {
|
||||
return f.writeFn(p)
|
||||
}
|
||||
|
||||
return 10, nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Close() error {
|
||||
if f != nil && f.closeFn != nil {
|
||||
return f.closeFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type StorageDriverMock struct {
|
||||
nameFn func() string
|
||||
getContentFn func(ctx context.Context, path string) ([]byte, error)
|
||||
putContentFn func(ctx context.Context, path string, content []byte) error
|
||||
readerFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error)
|
||||
writerFn func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error)
|
||||
statFn func(ctx context.Context, path string) (storageDriver.FileInfo, error)
|
||||
listFn func(ctx context.Context, path string) ([]string, error)
|
||||
moveFn func(ctx context.Context, sourcePath string, destPath string) error
|
||||
deleteFn func(ctx context.Context, path string) error
|
||||
walkFn func(ctx context.Context, path string, f storageDriver.WalkFn) error
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Name() string {
|
||||
if s != nil && s.nameFn != nil {
|
||||
return s.nameFn()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) {
|
||||
if s != nil && s.getContentFn != nil {
|
||||
return s.getContentFn(ctx, path)
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error {
|
||||
if s != nil && s.putContentFn != nil {
|
||||
return s.putContentFn(ctx, path, content)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
if s != nil && s.readerFn != nil {
|
||||
return s.readerFn(ctx, path, offset)
|
||||
}
|
||||
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Writer(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
if s != nil && s.writerFn != nil {
|
||||
return s.writerFn(ctx, path, append)
|
||||
}
|
||||
|
||||
return &FileWriterMock{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Stat(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
if s != nil && s.statFn != nil {
|
||||
return s.statFn(ctx, path)
|
||||
}
|
||||
|
||||
return &FileInfoMock{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) {
|
||||
if s != nil && s.listFn != nil {
|
||||
return s.listFn(ctx, path)
|
||||
}
|
||||
|
||||
return []string{"a"}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Move(ctx context.Context, sourcePath string, destPath string) error {
|
||||
if s != nil && s.moveFn != nil {
|
||||
return s.moveFn(ctx, sourcePath, destPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Delete(ctx context.Context, path string) error {
|
||||
if s != nil && s.deleteFn != nil {
|
||||
return s.deleteFn(ctx, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Walk(ctx context.Context, path string, f storageDriver.WalkFn) error {
|
||||
if s != nil && s.walkFn != nil {
|
||||
return s.walkFn(ctx, path, f)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNegativeCasesObjectsStorage(t *testing.T) {
|
||||
skipIt(t)
|
||||
|
||||
uuid, err := guuid.NewV4()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testDir := path.Join("/oci-repo-test", uuid.String())
|
||||
|
||||
store, il, _ := createObjectsStore(testDir)
|
||||
defer cleanupStorage(store, testDir)
|
||||
|
||||
Convey("Invalid validate repo", t, func(c C) {
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
objects, err := store.List(context.Background(), path.Join(il.RootDir(), testImage))
|
||||
So(err, ShouldBeNil)
|
||||
for _, object := range objects {
|
||||
t.Logf("Removing object: %s", object)
|
||||
err := store.Delete(context.Background(), object)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
_, err = il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
_, err = il.GetRepositories()
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid get image tags", t, func(c C) {
|
||||
store, il, err := createObjectsStore(testDir)
|
||||
defer cleanupStorage(store, testDir)
|
||||
So(err, ShouldBeNil)
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
|
||||
So(store.Move(context.Background(), path.Join(testDir, testImage, "index.json"),
|
||||
path.Join(testDir, testImage, "blobs")), ShouldBeNil)
|
||||
ok, _ := il.ValidateRepo(testImage)
|
||||
So(ok, ShouldBeFalse)
|
||||
_, err = il.GetImageTags(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
So(store.Delete(context.Background(), path.Join(testDir, testImage)), ShouldBeNil)
|
||||
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
So(store.PutContent(context.Background(), path.Join(testDir, testImage, "index.json"), []byte{}), ShouldBeNil)
|
||||
_, err = il.GetImageTags(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid get image manifest", t, func(c C) {
|
||||
store, il, err := createObjectsStore(testDir)
|
||||
defer cleanupStorage(store, testDir)
|
||||
So(err, ShouldBeNil)
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
So(store.Delete(context.Background(), path.Join(testDir, testImage, "index.json")), ShouldBeNil)
|
||||
_, _, _, err = il.GetImageManifest(testImage, "")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(store.Delete(context.Background(), path.Join(testDir, testImage)), ShouldBeNil)
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
So(store.PutContent(context.Background(), path.Join(testDir, testImage, "index.json"), []byte{}), ShouldBeNil)
|
||||
_, _, _, err = il.GetImageManifest(testImage, "")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid validate repo", t, func(c C) {
|
||||
store, il, err := createObjectsStore(testDir)
|
||||
defer cleanupStorage(store, testDir)
|
||||
So(err, ShouldBeNil)
|
||||
So(il, ShouldNotBeNil)
|
||||
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
So(store.Delete(context.Background(), path.Join(testDir, testImage, "index.json")), ShouldBeNil)
|
||||
_, err = il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(store.Delete(context.Background(), path.Join(testDir, testImage)), ShouldBeNil)
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
So(store.Move(context.Background(), path.Join(testDir, testImage, "index.json"),
|
||||
path.Join(testDir, testImage, "_index.json")), ShouldBeNil)
|
||||
ok, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldBeNil)
|
||||
So(ok, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Invalid finish blob upload", t, func(c C) {
|
||||
store, il, err := createObjectsStore(testDir)
|
||||
defer cleanupStorage(store, testDir)
|
||||
So(err, ShouldBeNil)
|
||||
So(il, ShouldNotBeNil)
|
||||
|
||||
So(il.InitRepo(testImage), ShouldBeNil)
|
||||
v, err := il.NewBlobUpload(testImage)
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content := []byte("test-data1")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
d := godigest.FromBytes(content)
|
||||
|
||||
b, err := il.PutBlobChunk(testImage, v, 0, int64(l), buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
src := il.BlobUploadPath(testImage, v)
|
||||
fw, err := store.Writer(context.Background(), src, true)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = fw.Write([]byte("another-chunk-of-data"))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = fw.Close()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = il.FinishBlobUpload(testImage, v, buf, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test storage driver errors", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{testImage}, errS3
|
||||
},
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), errS3
|
||||
},
|
||||
walkFn: func(ctx context.Context, path string, f storageDriver.WalkFn) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
return &FileInfoMock{}, errS3
|
||||
},
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
So(il, ShouldNotBeNil)
|
||||
|
||||
So(il.InitRepo(testImage), ShouldNotBeNil)
|
||||
_, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
v, err := il.NewBlobUpload(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
content := []byte("test-data1")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
d := godigest.FromBytes(content)
|
||||
|
||||
_, err = il.PutBlobChunk(testImage, v, 0, int64(l), buf)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = il.FinishBlobUpload(testImage, v, buf, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = il.DeleteBlob(testImage, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = il.DeleteBlobUpload(testImage, v)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = il.DeleteImageManifest(testImage, "1.0")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = il.PutImageManifest(testImage, "1.0", "application/json", []byte{})
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = il.PutBlobChunkStreamed(testImage, v, bytes.NewBuffer([]byte(testImage)))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, _, err = il.FullBlobUpload(testImage, bytes.NewBuffer([]byte{}), "inexistent")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, _, err = il.CheckBlob(testImage, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test ValidateRepo", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{testImage, testImage}, errS3
|
||||
},
|
||||
})
|
||||
_, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test ValidateRepo2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
})
|
||||
_, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test ValidateRepo3", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
})
|
||||
_, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test ValidateRepo4", t, func(c C) {
|
||||
ociLayout := []byte(`{"imageLayoutVersion": "9.9.9"}`)
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return ociLayout, nil
|
||||
},
|
||||
})
|
||||
_, err := il.ValidateRepo(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test GetRepositories", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
walkFn: func(ctx context.Context, path string, f storageDriver.WalkFn) error {
|
||||
return f(new(FileInfoMock))
|
||||
},
|
||||
})
|
||||
repos, err := il.GetRepositories()
|
||||
So(repos, ShouldBeEmpty)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Test DeleteImageManifest", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
})
|
||||
err := il.DeleteImageManifest(testImage, "1.0")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test DeleteImageManifest2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{})
|
||||
err := il.DeleteImageManifest(testImage, "1.0")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test NewBlobUpload", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
_, err := il.NewBlobUpload(testImage)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test GetBlobUpload", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (storageDriver.FileInfo, error) {
|
||||
return &FileInfoMock{}, errS3
|
||||
},
|
||||
})
|
||||
_, err := il.GetBlobUpload(testImage, "uuid")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test PutBlobChunkStreamed", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
_, err := il.PutBlobChunkStreamed(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test PutBlobChunkStreamed2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{writeFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
}}, nil
|
||||
},
|
||||
})
|
||||
_, err := il.PutBlobChunkStreamed(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test PutBlobChunk", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
_, err := il.PutBlobChunk(testImage, "uuid", 0, 100, ioutil.NopCloser(strings.NewReader("")))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test PutBlobChunk2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
writeFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
},
|
||||
cancelFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
_, err := il.PutBlobChunk(testImage, "uuid", 0, 100, ioutil.NopCloser(strings.NewReader("")))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test PutBlobChunk3", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
writeFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
_, err := il.PutBlobChunk(testImage, "uuid", 12, 100, ioutil.NopCloser(strings.NewReader("")))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FinishBlobUpload", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
commitFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte("test"))
|
||||
err := il.FinishBlobUpload(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FinishBlobUpload2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
closeFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte("test"))
|
||||
err := il.FinishBlobUpload(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FinishBlobUpload3", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return nil, errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte("test"))
|
||||
err := il.FinishBlobUpload(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FinishBlobUpload4", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte(""))
|
||||
err := il.FinishBlobUpload(testImage, "uuid", ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FullBlobUpload", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, append bool) (storageDriver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte(""))
|
||||
_, _, err := il.FullBlobUpload(testImage, ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FullBlobUpload2", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{})
|
||||
d := godigest.FromBytes([]byte(" "))
|
||||
_, _, err := il.FullBlobUpload(testImage, ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test FullBlobUpload3", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte(""))
|
||||
_, _, err := il.FullBlobUpload(testImage, ioutil.NopCloser(strings.NewReader("")), d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test GetBlob", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte(""))
|
||||
_, _, err := il.GetBlob(testImage, d.String(), "")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Test DeleteBlob", t, func(c C) {
|
||||
il = createMockStorage(testDir, &StorageDriverMock{
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
d := godigest.FromBytes([]byte(""))
|
||||
err := il.DeleteBlob(testImage, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,10 @@ import (
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
S3StorageDriverName = "s3"
|
||||
)
|
||||
|
||||
type ImageStore interface {
|
||||
DirExists(d string) bool
|
||||
RootDir() string
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
const (
|
||||
// BlobUploadDir defines the upload directory for blob uploads.
|
||||
BlobUploadDir = ".uploads"
|
||||
schemaVersion = 2
|
||||
SchemaVersion = 2
|
||||
gcDelay = 1 * time.Hour
|
||||
)
|
||||
|
||||
@@ -463,7 +463,7 @@ func (is *ImageStoreFS) PutImageManifest(repo string, reference string, mediaTyp
|
||||
return "", errors.ErrBadManifest
|
||||
}
|
||||
|
||||
if m.SchemaVersion != schemaVersion {
|
||||
if m.SchemaVersion != SchemaVersion {
|
||||
is.log.Error().Int("SchemaVersion", m.SchemaVersion).Msg("invalid manifest")
|
||||
return "", errors.ErrBadManifest
|
||||
}
|
||||
|
||||
@@ -0,0 +1,749 @@
|
||||
package storage_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "crypto/sha256"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/anuvu/zot/errors"
|
||||
"github.com/anuvu/zot/pkg/extensions/monitoring"
|
||||
"github.com/anuvu/zot/pkg/log"
|
||||
"github.com/anuvu/zot/pkg/storage"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/rs/zerolog"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestStorageFSAPIs(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
Convey("Repo layout", t, func(c C) {
|
||||
repoName := "test"
|
||||
|
||||
Convey("Bad image manifest", func() {
|
||||
v, err := il.NewBlobUpload("test")
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content := []byte("test-data1")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
d := godigest.FromBytes(content)
|
||||
|
||||
b, err := il.PutBlobChunk(repoName, v, 0, int64(l), buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
err = il.FinishBlobUpload("test", v, buf, d.String())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
annotationsMap := make(map[string]string)
|
||||
annotationsMap[ispec.AnnotationRefName] = "1.0"
|
||||
m := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
MediaType: "application/vnd.oci.image.layer.v1.tar",
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
},
|
||||
Annotations: annotationsMap,
|
||||
}
|
||||
|
||||
m.SchemaVersion = 2
|
||||
mb, _ := json.Marshal(m)
|
||||
d = godigest.FromBytes(mb)
|
||||
|
||||
err = os.Chmod(path.Join(il.RootDir(), repoName, "index.json"), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, mb)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(il.RootDir(), repoName, "index.json"), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, mb)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestPath := path.Join(il.RootDir(), repoName, "blobs", d.Algorithm().String(), d.Encoded())
|
||||
|
||||
err = os.Chmod(manifestPath, 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, _, _, err = il.GetImageManifest(repoName, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Remove(manifestPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, _, _, err = il.GetImageManifest(repoName, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(il.RootDir(), repoName), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.PutImageManifest(repoName, "2.0", ispec.MediaTypeImageManifest, mb)
|
||||
So(err, ShouldNotBeNil)
|
||||
err = os.Chmod(path.Join(il.RootDir(), repoName), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// invalid DeleteImageManifest
|
||||
indexPath := path.Join(il.RootDir(), repoName, "index.json")
|
||||
err = os.Chmod(indexPath, 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = il.DeleteImageManifest(repoName, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.RemoveAll(path.Join(il.RootDir(), repoName))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDedupeLinks(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
Convey("Dedupe", t, func(c C) {
|
||||
blobDigest1 := ""
|
||||
blobDigest2 := ""
|
||||
|
||||
// manifest1
|
||||
v, err := il.NewBlobUpload("dedupe1")
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content := []byte("test-data3")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
d := godigest.FromBytes(content)
|
||||
b, err := il.PutBlobChunkStreamed("dedupe1", v, buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
blobDigest1 = strings.Split(d.String(), ":")[1]
|
||||
So(blobDigest1, ShouldNotBeEmpty)
|
||||
|
||||
err = il.FinishBlobUpload("dedupe1", v, buf, d.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
_, _, err = il.CheckBlob("dedupe1", d.String())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, err = il.GetBlob("dedupe1", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
m := ispec.Manifest{}
|
||||
m.SchemaVersion = 2
|
||||
m = ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
MediaType: "application/vnd.oci.image.layer.v1.tar",
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
},
|
||||
}
|
||||
m.SchemaVersion = 2
|
||||
mb, _ := json.Marshal(m)
|
||||
d = godigest.FromBytes(mb)
|
||||
_, err = il.PutImageManifest("dedupe1", d.String(), ispec.MediaTypeImageManifest, mb)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = il.GetImageManifest("dedupe1", d.String())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// manifest2
|
||||
v, err = il.NewBlobUpload("dedupe2")
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content = []byte("test-data3")
|
||||
buf = bytes.NewBuffer(content)
|
||||
l = buf.Len()
|
||||
d = godigest.FromBytes(content)
|
||||
b, err = il.PutBlobChunkStreamed("dedupe2", v, buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
blobDigest2 = strings.Split(d.String(), ":")[1]
|
||||
So(blobDigest2, ShouldNotBeEmpty)
|
||||
|
||||
err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
_, _, err = il.CheckBlob("dedupe2", d.String())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, err = il.GetBlob("dedupe2", d.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
m = ispec.Manifest{}
|
||||
m.SchemaVersion = 2
|
||||
m = ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
MediaType: "application/vnd.oci.image.layer.v1.tar",
|
||||
Digest: d,
|
||||
Size: int64(l),
|
||||
},
|
||||
},
|
||||
}
|
||||
m.SchemaVersion = 2
|
||||
mb, _ = json.Marshal(m)
|
||||
d = godigest.FromBytes(mb)
|
||||
_, err = il.PutImageManifest("dedupe2", "1.0", ispec.MediaTypeImageManifest, mb)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = il.GetImageManifest("dedupe2", d.String())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// verify that dedupe with hard links happened
|
||||
fi1, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest1))
|
||||
So(err, ShouldBeNil)
|
||||
fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2))
|
||||
So(err, ShouldBeNil)
|
||||
So(os.SameFile(fi1, fi2), ShouldBeTrue)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDedupe(t *testing.T) {
|
||||
Convey("Dedupe", t, func(c C) {
|
||||
Convey("Nil ImageStore", func() {
|
||||
var is storage.ImageStore
|
||||
So(func() { _ = is.DedupeBlob("", "", "") }, ShouldPanic)
|
||||
})
|
||||
|
||||
Convey("Valid ImageStore", func() {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
So(il.DedupeBlob("", "", ""), ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
func TestNegativeCases(t *testing.T) {
|
||||
Convey("Invalid root dir", t, func(c C) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
|
||||
So(storage.NewImageStore(dir, true, true, log, metrics), ShouldNotBeNil)
|
||||
if os.Geteuid() != 0 {
|
||||
So(storage.NewImageStore("/deadBEEF", true, true, log, metrics), ShouldBeNil)
|
||||
}
|
||||
})
|
||||
|
||||
Convey("Invalid init repo", t, func(c C) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
err = os.Chmod(dir, 0000) // remove all perms
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
err = il.InitRepo("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
}
|
||||
|
||||
err = os.Chmod(dir, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Init repo should fail if repo is a file.
|
||||
err = ioutil.WriteFile(path.Join(dir, "file-test"), []byte("this is test file"), 0755) // nolint:gosec
|
||||
So(err, ShouldBeNil)
|
||||
err = il.InitRepo("file-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Mkdir(path.Join(dir, "test-dir"), 0755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = il.InitRepo("test-dir")
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid validate repo", t, func(c C) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
|
||||
err = os.MkdirAll(path.Join(dir, "invalid-test"), 0755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "invalid-test"), 0000) // remove all perms
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = il.ValidateRepo("invalid-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, errors.ErrRepoNotFound)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "invalid-test"), 0755) // remove all perms
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "invalid-test", "blobs"), []byte{}, 0755) // nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "invalid-test", "index.json"), []byte{}, 0755) // nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte{}, 0755) // nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
isValid, err := il.ValidateRepo("invalid-test")
|
||||
So(err, ShouldBeNil)
|
||||
So(isValid, ShouldEqual, false)
|
||||
|
||||
err = os.Remove(path.Join(dir, "invalid-test", "blobs"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = os.Mkdir(path.Join(dir, "invalid-test", "blobs"), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
isValid, err = il.ValidateRepo("invalid-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(isValid, ShouldEqual, false)
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte("{}"), 0755) // nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
isValid, err = il.ValidateRepo("invalid-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, errors.ErrRepoBadVersion)
|
||||
So(isValid, ShouldEqual, false)
|
||||
|
||||
files, err := ioutil.ReadDir(path.Join(dir, "test"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
os.Remove(path.Join(dir, "test", f.Name()))
|
||||
}
|
||||
|
||||
_, err = il.ValidateRepo("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.RemoveAll(path.Join(dir, "test"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.ValidateRepo("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(dir, 0000) // remove all perms
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
So(func() { _, _ = il.ValidateRepo("test") }, ShouldPanic)
|
||||
}
|
||||
|
||||
err = os.Chmod(dir, 0755) // remove all perms
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.GetRepositories()
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid get image tags", t, func(c C) {
|
||||
var ilfs storage.ImageStoreFS
|
||||
_, err := ilfs.GetImageTags("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
So(os.Remove(path.Join(dir, "test", "index.json")), ShouldBeNil)
|
||||
_, err = il.GetImageTags("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil)
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
So(ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600), ShouldBeNil)
|
||||
_, err = il.GetImageTags("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid get image manifest", t, func(c C) {
|
||||
var ilfs storage.ImageStoreFS
|
||||
_, _, _, err := ilfs.GetImageManifest("test", "")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test", "index.json"), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, _, _, err = il.GetImageManifest("test", "")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Remove(path.Join(dir, "test", "index.json"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, _, _, err = il.GetImageManifest("test", "")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.RemoveAll(path.Join(dir, "test"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, _, _, err = il.GetImageManifest("test", "")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid new blob upload", t, func(c C) {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
So(il, ShouldNotBeNil)
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test", ".uploads"), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = il.NewBlobUpload("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test"), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = il.NewBlobUpload("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test"), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
So(il.InitRepo("test"), ShouldBeNil)
|
||||
|
||||
_, err = il.NewBlobUpload("test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test", ".uploads"), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
v, err := il.NewBlobUpload("test")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(dir, "test", ".uploads"), 0000)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
content := []byte("test-data3")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
_, err = il.PutBlobChunkStreamed("test", v, buf)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
_, err = il.PutBlobChunk("test", v, 0, int64(l), buf)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Invalid dedupe sceanrios", t, func() {
|
||||
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
log := log.Logger{Logger: zerolog.New(os.Stdout)}
|
||||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
il := storage.NewImageStore(dir, true, true, log, metrics)
|
||||
|
||||
v, err := il.NewBlobUpload("dedupe1")
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content := []byte("test-data3")
|
||||
buf := bytes.NewBuffer(content)
|
||||
l := buf.Len()
|
||||
d := godigest.FromBytes(content)
|
||||
b, err := il.PutBlobChunkStreamed("dedupe1", v, buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
blobDigest1 := strings.Split(d.String(), ":")[1]
|
||||
So(blobDigest1, ShouldNotBeEmpty)
|
||||
|
||||
err = il.FinishBlobUpload("dedupe1", v, buf, d.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
// Create a file at the same place where FinishBlobUpload will create
|
||||
err = il.InitRepo("dedupe2")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.MkdirAll(path.Join(dir, "dedupe2", "blobs/sha256"), 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1), content, 0755) // nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
v, err = il.NewBlobUpload("dedupe2")
|
||||
So(err, ShouldBeNil)
|
||||
So(v, ShouldNotBeEmpty)
|
||||
|
||||
content = []byte("test-data3")
|
||||
buf = bytes.NewBuffer(content)
|
||||
l = buf.Len()
|
||||
d = godigest.FromBytes(content)
|
||||
b, err = il.PutBlobChunkStreamed("dedupe2", v, buf)
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
cmd := exec.Command("sudo", "chattr", "+i", path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1)) // nolint: gosec
|
||||
_, err = cmd.Output()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
|
||||
So(err, ShouldNotBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
|
||||
cmd = exec.Command("sudo", "chattr", "-i", path.Join(dir, "dedupe2", "blobs/sha256", blobDigest1)) // nolint: gosec
|
||||
_, err = cmd.Output()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = il.FinishBlobUpload("dedupe2", v, buf, d.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(b, ShouldEqual, l)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHardLink(t *testing.T) {
|
||||
Convey("Test that ValidateHardLink creates rootDir if it does not exist", t, func() {
|
||||
var randomDir string
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for {
|
||||
randomLen := rand.Intn(100)
|
||||
randomDir = "/tmp/" + randSeq(randomLen)
|
||||
|
||||
if _, err := os.Stat(randomDir); os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(randomDir)
|
||||
|
||||
err := storage.ValidateHardLink(randomDir)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
Convey("Test that ValidateHardLink returns error if rootDir is a file", t, func() {
|
||||
dir, err := ioutil.TempDir("", "storage-hard-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
filePath := path.Join(dir, "file.txt")
|
||||
err = ioutil.WriteFile(filePath, []byte("some dummy file content"), 0644) //nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = storage.ValidateHardLink(filePath)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("Test if filesystem supports hardlink", t, func() {
|
||||
dir, err := ioutil.TempDir("", "storage-hard-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
err = storage.ValidateHardLink(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = ioutil.WriteFile(path.Join(dir, "hardtest.txt"), []byte("testing hard link code"), 0644) //nolint: gosec
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.Chmod(dir, 0400)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.Link(path.Join(dir, "hardtest.txt"), path.Join(dir, "duphardtest.txt"))
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(dir, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func randSeq(n int) string {
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
+614
-1036
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user