mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 20:38:08 +08:00
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:
committed by
Ramkumar Chinchani
parent
9ca6eea940
commit
28974e81dc
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user