config: support multiple storage locations

added support to point multiple storage locations in zot by running multiple instance of zot in background.

see examples/config-multiple.json for more info about config.

Closes #181
This commit is contained in:
Shivam Mishra
2021-04-05 17:40:33 -07:00
committed by Ramkumar Chinchani
parent 9ca6eea940
commit 28974e81dc
21 changed files with 1810 additions and 164 deletions
+69
View File
@@ -10,6 +10,7 @@ import (
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
@@ -37,6 +38,11 @@ type BlobUpload struct {
ID string
}
type StoreController struct {
DefaultStore *ImageStore
SubStore map[string]*ImageStore
}
// ImageStore provides the image storage operations.
type ImageStore struct {
rootDir string
@@ -48,6 +54,39 @@ type ImageStore struct {
log zerolog.Logger
}
func (is *ImageStore) RootDir() string {
return is.rootDir
}
func getRoutePrefix(name string) string {
names := strings.SplitN(name, "/", 2)
if len(names) != 2 { // nolint: gomnd
// it means route is of global storage e.g "centos:latest"
if len(names) == 1 {
return "/"
}
}
return fmt.Sprintf("/%s", names[0])
}
func (sc StoreController) GetImageStore(name string) *ImageStore {
if sc.SubStore != nil {
// SubStore is being provided, now we need to find equivalent image store and this will be found by splitting name
prefixName := getRoutePrefix(name)
imgStore, ok := sc.SubStore[prefixName]
if !ok {
imgStore = sc.DefaultStore
}
return imgStore
}
return sc.DefaultStore
}
// NewImageStore returns a new image store backed by a file storage.
func NewImageStore(rootDir string, gc bool, dedupe bool, log zlog.Logger) *ImageStore {
if _, err := os.Stat(rootDir); os.IsNotExist(err) {
@@ -1174,6 +1213,36 @@ func Scrub(dir string, fix bool) error {
// utility routines
func CheckHardLink(srcFileName string, destFileName string) error {
return os.Link(srcFileName, destFileName)
}
func ValidateHardLink(rootDir string) error {
err := ioutil.WriteFile(path.Join(rootDir, "hardlinkcheck.txt"), //nolint: gosec
[]byte("check whether hardlinks work on filesystem"), 0644)
if err != nil {
return err
}
err = CheckHardLink(path.Join(rootDir, "hardlinkcheck.txt"), path.Join(rootDir, "duphardlinkcheck.txt"))
if err != nil {
// Remove hardlinkcheck.txt if hardlink fails
zerr := os.RemoveAll(path.Join(rootDir, "hardlinkcheck.txt"))
if zerr != nil {
return zerr
}
return err
}
err = os.RemoveAll(path.Join(rootDir, "hardlinkcheck.txt"))
if err != nil {
return err
}
return os.RemoveAll(path.Join(rootDir, "duphardlinkcheck.txt"))
}
func dirExists(d string) bool {
fi, err := os.Stat(d)
if err != nil && os.IsNotExist(err) {
+86
View File
@@ -598,3 +598,89 @@ func TestNegativeCases(t *testing.T) {
So(err, ShouldNotBeNil)
})
}
func TestHardLink(t *testing.T) {
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 = storage.CheckHardLink(path.Join(dir, "hardtest.txt"), path.Join(dir, "duphardtest.txt"))
So(err, ShouldNotBeNil)
err = os.Chmod(dir, 0644)
if err != nil {
panic(err)
}
})
}
func TestStorageHandler(t *testing.T) {
Convey("Test storage handler", t, func() {
// Create temporary directory
firstRootDir, err := ioutil.TempDir("", "util_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(firstRootDir)
secondRootDir, err := ioutil.TempDir("", "util_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(secondRootDir)
thirdRootDir, err := ioutil.TempDir("", "util_test")
if err != nil {
panic(err)
}
defer os.RemoveAll(thirdRootDir)
log := log.NewLogger("debug", "")
// Create ImageStore
firstStore := storage.NewImageStore(firstRootDir, false, false, log)
secondStore := storage.NewImageStore(secondRootDir, false, false, log)
thirdStore := storage.NewImageStore(thirdRootDir, false, false, log)
storeController := storage.StoreController{}
storeController.DefaultStore = firstStore
subStore := make(map[string]*storage.ImageStore)
subStore["/a"] = secondStore
subStore["/b"] = thirdStore
storeController.SubStore = subStore
is := storeController.GetImageStore("zot-x-test")
So(is.RootDir(), ShouldEqual, firstRootDir)
is = storeController.GetImageStore("a/zot-a-test")
So(is.RootDir(), ShouldEqual, secondRootDir)
is = storeController.GetImageStore("b/zot-b-test")
So(is.RootDir(), ShouldEqual, thirdRootDir)
is = storeController.GetImageStore("c/zot-c-test")
So(is.RootDir(), ShouldEqual, firstRootDir)
})
}