store: add support to split ImageStore
Allow storage users to split the filesystem of containers vs image store, `imagestore` if configured will pull images in image storage instead of the `graphRoot` while keeping the other parts still in the originally configured `graphRoot`. overlay: set workdir and upperdir according to splitstore If splitstore is set `workdir` and `upperdir` must go into the splitstore i.e `graphRoot`. Signed-off-by: Aditya R <arajan@redhat.com>
This commit is contained in:
parent
21aca2978d
commit
30775d4b2b
|
|
@ -43,6 +43,7 @@ func main() {
|
|||
flags := mflag.NewFlagSet(command, eh)
|
||||
flags.StringVar(&options.RunRoot, []string{"-run", "R"}, options.RunRoot, "Root of the runtime state tree")
|
||||
flags.StringVar(&options.GraphRoot, []string{"-graph", "g"}, options.GraphRoot, "Root of the storage tree")
|
||||
flags.StringVar(&options.ImageStore, []string{"-image-store"}, options.ImageStore, "Root of the separate image store")
|
||||
flags.BoolVar(&options.TransientStore, []string{"-transient-store"}, options.TransientStore, "Transient store")
|
||||
flags.StringVar(&options.GraphDriverName, []string{"-storage-driver", "s"}, options.GraphDriverName, "Storage driver to use ($STORAGE_DRIVER)")
|
||||
flags.Var(opts.NewListOptsRef(&options.GraphDriverOptions, nil), []string{"-storage-opt"}, "Set storage driver options ($STORAGE_OPTS)")
|
||||
|
|
|
|||
|
|
@ -55,6 +55,11 @@ $ restorecon -R -v /NEWSTORAGEPATH
|
|||
|
||||
A common use case for this field is to provide a local storage directory when user home directories are NFS-mounted (podman does not support container storage over NFS).
|
||||
|
||||
**imagestore**=""
|
||||
Path of imagestore different from `graphroot`, by default storage library stores all images in `graphroot` but if `imagestore` is provided it will store newly pulled images in provided `imagestore` but will keep using `graphroot` for everything else. If user is using `overlay` driver then images which were already part of `graphroot` will still be accessible ( Internally storage library will mount `graphroot` as an `additionalImageStore` to allow this behaviour ).
|
||||
|
||||
A common use case for this field is for the users who want to split the file-system in different parts i.e disk which stores images vs disk used by the container created by the image.
|
||||
|
||||
**runroot**=""
|
||||
container storage run dir (default: "/run/containers/storage")
|
||||
Default directory to store all temporary writable content created by container storage programs. The rootless runroot path supports environment variable substitutions (ie. `$HOME/containers/storage`)
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ func getBuiltinDriver(name, home string, options Options) (Driver, error) {
|
|||
type Options struct {
|
||||
Root string
|
||||
RunRoot string
|
||||
ImageStore string
|
||||
DriverPriority []string
|
||||
DriverOptions []string
|
||||
UIDMaps []idtools.IDMap
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ type Driver struct {
|
|||
name string
|
||||
home string
|
||||
runhome string
|
||||
imageStore string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
ctr *graphdriver.RefCounter
|
||||
|
|
@ -304,6 +305,16 @@ func isNetworkFileSystem(fsMagic graphdriver.FsMagic) bool {
|
|||
// If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error.
|
||||
// If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned.
|
||||
func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) {
|
||||
// If custom --imagestore is selected never
|
||||
// ditch the original graphRoot, instead add it as
|
||||
// additionalImageStore so its images can still be
|
||||
// read and used.
|
||||
if options.ImageStore != "" {
|
||||
graphRootAsAdditionalStore := fmt.Sprintf("AdditionalImageStore=%s", options.ImageStore)
|
||||
options.DriverOptions = append(options.DriverOptions, graphRootAsAdditionalStore)
|
||||
// complete base name with driver name included
|
||||
options.ImageStore = filepath.Join(options.ImageStore, "overlay")
|
||||
}
|
||||
opts, err := parseOptions(options.DriverOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -330,6 +341,12 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if options.ImageStore != "" {
|
||||
if err := idtools.MkdirAllAs(path.Join(options.ImageStore, linkDir), 0755, 0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := idtools.MkdirAllAs(runhome, 0700, rootUID, rootGID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -421,6 +438,7 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
|
|||
d := &Driver{
|
||||
name: "overlay",
|
||||
home: home,
|
||||
imageStore: options.ImageStore,
|
||||
runhome: runhome,
|
||||
uidMaps: options.UIDMaps,
|
||||
gidMaps: options.GIDMaps,
|
||||
|
|
@ -807,15 +825,22 @@ func (d *Driver) Status() [][2]string {
|
|||
// Metadata returns meta data about the overlay driver such as
|
||||
// LowerDir, UpperDir, WorkDir and MergeDir used to store data.
|
||||
func (d *Driver) Metadata(id string) (map[string]string, error) {
|
||||
dir := d.dir(id)
|
||||
dir, imagestore, _ := d.dir2(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workDirBase := dir
|
||||
if imagestore != "" {
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workDirBase = imagestore
|
||||
}
|
||||
|
||||
metadata := map[string]string{
|
||||
"WorkDir": path.Join(dir, "work"),
|
||||
"MergedDir": path.Join(dir, "merged"),
|
||||
"UpperDir": path.Join(dir, "diff"),
|
||||
"WorkDir": path.Join(workDirBase, "work"),
|
||||
"MergedDir": path.Join(workDirBase, "merged"),
|
||||
"UpperDir": path.Join(workDirBase, "diff"),
|
||||
}
|
||||
|
||||
lowerDirs, err := d.getLowerDirs(id)
|
||||
|
|
@ -930,7 +955,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
|
|||
}
|
||||
|
||||
func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disableQuota bool) (retErr error) {
|
||||
dir := d.dir(id)
|
||||
dir, imageStore, _ := d.dir2(id)
|
||||
|
||||
uidMaps := d.uidMaps
|
||||
gidMaps := d.gidMaps
|
||||
|
|
@ -958,8 +983,19 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
|
|||
if err := idtools.MkdirAllAndChownNew(path.Dir(dir), 0755, idPair); err != nil {
|
||||
return err
|
||||
}
|
||||
workDirBase := dir
|
||||
if imageStore != "" {
|
||||
workDirBase = imageStore
|
||||
if err := idtools.MkdirAllAndChownNew(path.Dir(imageStore), 0755, idPair); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if parent != "" {
|
||||
st, err := system.Stat(filepath.Join(d.dir(parent), "diff"))
|
||||
parentBase, parentImageStore, _ := d.dir2(parent)
|
||||
if parentImageStore != "" {
|
||||
parentBase = parentImageStore
|
||||
}
|
||||
st, err := system.Stat(filepath.Join(parentBase, "diff"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -979,6 +1015,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
|
|||
if err := idtools.MkdirAllAndChownNew(dir, 0700, idPair); err != nil {
|
||||
return err
|
||||
}
|
||||
if imageStore != "" {
|
||||
if err := idtools.MkdirAllAndChownNew(imageStore, 0700, idPair); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Clean up on failure
|
||||
|
|
@ -986,6 +1027,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
|
|||
if err2 := os.RemoveAll(dir); err2 != nil {
|
||||
logrus.Errorf("While recovering from a failure creating a layer, error deleting %#v: %v", dir, err2)
|
||||
}
|
||||
if imageStore != "" {
|
||||
if err2 := os.RemoveAll(workDirBase); err2 != nil {
|
||||
logrus.Errorf("While recovering from a failure creating a layer, error deleting %#v: %v", dir, err2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -1014,20 +1060,31 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
|
|||
if d.options.forceMask != nil {
|
||||
perms = *d.options.forceMask
|
||||
}
|
||||
|
||||
if parent != "" {
|
||||
st, err := system.Stat(filepath.Join(d.dir(parent), "diff"))
|
||||
parentDir, parentImageStore, _ := d.dir2(parent)
|
||||
base := parentDir
|
||||
if parentImageStore != "" {
|
||||
base = parentImageStore
|
||||
}
|
||||
st, err := system.Stat(filepath.Join(base, "diff"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
perms = os.FileMode(st.Mode())
|
||||
}
|
||||
|
||||
if err := idtools.MkdirAs(path.Join(dir, "diff"), perms, rootUID, rootGID); err != nil {
|
||||
if err := idtools.MkdirAs(path.Join(workDirBase, "diff"), perms, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lid := generateID(idLength)
|
||||
if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil {
|
||||
|
||||
linkBase := path.Join("..", id, "diff")
|
||||
if imageStore != "" {
|
||||
linkBase = path.Join(imageStore, "diff")
|
||||
}
|
||||
if err := os.Symlink(linkBase, path.Join(d.home, linkDir, lid)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -1036,7 +1093,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, disable
|
|||
return err
|
||||
}
|
||||
|
||||
if err := idtools.MkdirAs(path.Join(dir, "work"), 0700, rootUID, rootGID); err != nil {
|
||||
if err := idtools.MkdirAs(path.Join(workDirBase, "work"), 0700, rootUID, rootGID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := idtools.MkdirAs(path.Join(dir, "merged"), 0700, rootUID, rootGID); err != nil {
|
||||
|
|
@ -1121,22 +1178,26 @@ func (d *Driver) getLower(parent string) (string, error) {
|
|||
}
|
||||
|
||||
func (d *Driver) dir(id string) string {
|
||||
p, _ := d.dir2(id)
|
||||
p, _, _ := d.dir2(id)
|
||||
return p
|
||||
}
|
||||
|
||||
func (d *Driver) dir2(id string) (string, bool) {
|
||||
func (d *Driver) dir2(id string) (string, string, bool) {
|
||||
newpath := path.Join(d.home, id)
|
||||
imageStore := ""
|
||||
if d.imageStore != "" {
|
||||
imageStore = path.Join(d.imageStore, id)
|
||||
}
|
||||
if _, err := os.Stat(newpath); err != nil {
|
||||
for _, p := range d.AdditionalImageStores() {
|
||||
l := path.Join(p, d.name, id)
|
||||
_, err = os.Stat(l)
|
||||
if err == nil {
|
||||
return l, true
|
||||
return l, imageStore, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return newpath, false
|
||||
return newpath, imageStore, false
|
||||
}
|
||||
|
||||
func (d *Driver) getLowerDirs(id string) ([]string, error) {
|
||||
|
|
@ -1343,10 +1404,14 @@ func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
|
|||
}
|
||||
|
||||
func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountOpts) (_ string, retErr error) {
|
||||
dir, inAdditionalStore := d.dir2(id)
|
||||
dir, imageStore, inAdditionalStore := d.dir2(id)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
workDirBase := dir
|
||||
if imageStore != "" {
|
||||
workDirBase = imageStore
|
||||
}
|
||||
readWrite := !inAdditionalStore
|
||||
|
||||
if !d.SupportsShifting() || options.DisableShifting {
|
||||
|
|
@ -1483,7 +1548,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
diffDir := path.Join(dir, "diff")
|
||||
diffDir := path.Join(workDirBase, "diff")
|
||||
if err := idtools.MkdirAllAs(diffDir, perms, rootUID, rootGID); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -1506,7 +1571,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
|
|||
}
|
||||
}()
|
||||
|
||||
workdir := path.Join(dir, "work")
|
||||
workdir := path.Join(workDirBase, "work")
|
||||
|
||||
if d.options.mountProgram == "" && unshare.IsRootless() {
|
||||
optsList = append(optsList, "userxattr")
|
||||
|
|
@ -1926,8 +1991,12 @@ func (d *Driver) ApplyDiff(id, parent string, options graphdriver.ApplyDiffOpts)
|
|||
}
|
||||
|
||||
func (d *Driver) getDiffPath(id string) (string, error) {
|
||||
dir := d.dir(id)
|
||||
return redirectDiffIfAdditionalLayer(path.Join(dir, "diff"))
|
||||
dir, imagestore, _ := d.dir2(id)
|
||||
base := dir
|
||||
if imagestore != "" {
|
||||
base = imagestore
|
||||
}
|
||||
return redirectDiffIfAdditionalLayer(path.Join(base, "diff"))
|
||||
}
|
||||
|
||||
func (d *Driver) getLowerDiffPaths(id string) ([]string, error) {
|
||||
|
|
@ -2018,8 +2087,12 @@ func (d *Driver) AdditionalImageStores() []string {
|
|||
// by toContainer to those specified by toHost.
|
||||
func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
|
||||
var err error
|
||||
dir := d.dir(id)
|
||||
diffDir := filepath.Join(dir, "diff")
|
||||
dir, imagestore, _ := d.dir2(id)
|
||||
base := dir
|
||||
if imagestore != "" {
|
||||
base = imagestore
|
||||
}
|
||||
diffDir := filepath.Join(base, "diff")
|
||||
|
||||
rootUID, rootGID := 0, 0
|
||||
if toHost != nil {
|
||||
|
|
|
|||
|
|
@ -124,6 +124,11 @@ type OptionsConfig struct {
|
|||
// for shared image content
|
||||
AdditionalImageStores []string `toml:"additionalimagestores,omitempty"`
|
||||
|
||||
// ImageStore is the location of image store which is separated from the
|
||||
// container store. Usually this is not recommended unless users wants
|
||||
// separate store for image and containers.
|
||||
ImageStore string `toml:"imagestore,omitempty"`
|
||||
|
||||
// AdditionalLayerStores is the location of additional read/only
|
||||
// Layer stores. Usually used to access Networked File System
|
||||
// for shared image content
|
||||
|
|
|
|||
33
store.go
33
store.go
|
|
@ -661,6 +661,7 @@ type store struct {
|
|||
usernsLock *lockfile.LockFile
|
||||
graphRoot string
|
||||
graphOptions []string
|
||||
imageStoreDir string
|
||||
pullOptions map[string]string
|
||||
uidMap []idtools.IDMap
|
||||
gidMap []idtools.IDMap
|
||||
|
|
@ -755,9 +756,19 @@ func GetStore(options types.StoreOptions) (Store, error) {
|
|||
if err := os.MkdirAll(options.GraphRoot, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.ImageStore != "" {
|
||||
if err := os.MkdirAll(options.ImageStore, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(options.GraphRoot, options.GraphDriverName), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.ImageStore != "" {
|
||||
if err := os.MkdirAll(filepath.Join(options.ImageStore, options.GraphDriverName), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
graphLock, err := lockfile.GetLockFile(filepath.Join(options.GraphRoot, "storage.lock"))
|
||||
if err != nil {
|
||||
|
|
@ -785,6 +796,7 @@ func GetStore(options types.StoreOptions) (Store, error) {
|
|||
usernsLock: usernsLock,
|
||||
graphRoot: options.GraphRoot,
|
||||
graphOptions: options.GraphDriverOptions,
|
||||
imageStoreDir: options.ImageStore,
|
||||
pullOptions: options.PullOptions,
|
||||
uidMap: copyIDMap(options.UIDMap),
|
||||
gidMap: copyIDMap(options.GIDMap),
|
||||
|
|
@ -889,7 +901,11 @@ func (s *store) load() error {
|
|||
}
|
||||
driverPrefix := s.graphDriverName + "-"
|
||||
|
||||
gipath := filepath.Join(s.graphRoot, driverPrefix+"images")
|
||||
imgStoreRoot := s.imageStoreDir
|
||||
if imgStoreRoot == "" {
|
||||
imgStoreRoot = s.graphRoot
|
||||
}
|
||||
gipath := filepath.Join(imgStoreRoot, driverPrefix+"images")
|
||||
if err := os.MkdirAll(gipath, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -989,8 +1005,15 @@ func (s *store) stopUsingGraphDriver() {
|
|||
// Almost all users should use startUsingGraphDriver instead.
|
||||
// The caller must hold s.graphLock.
|
||||
func (s *store) createGraphDriverLocked() (drivers.Driver, error) {
|
||||
driverRoot := s.imageStoreDir
|
||||
imageStoreBase := s.graphRoot
|
||||
if driverRoot == "" {
|
||||
driverRoot = s.graphRoot
|
||||
imageStoreBase = ""
|
||||
}
|
||||
config := drivers.Options{
|
||||
Root: s.graphRoot,
|
||||
Root: driverRoot,
|
||||
ImageStore: imageStoreBase,
|
||||
RunRoot: s.runRoot,
|
||||
DriverPriority: s.graphDriverPriority,
|
||||
DriverOptions: s.graphOptions,
|
||||
|
|
@ -1020,7 +1043,11 @@ func (s *store) getLayerStoreLocked() (rwLayerStore, error) {
|
|||
if err := os.MkdirAll(rlpath, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glpath := filepath.Join(s.graphRoot, driverPrefix+"layers")
|
||||
imgStoreRoot := s.imageStoreDir
|
||||
if imgStoreRoot == "" {
|
||||
imgStoreRoot = s.graphRoot
|
||||
}
|
||||
glpath := filepath.Join(imgStoreRoot, driverPrefix+"layers")
|
||||
if err := os.MkdirAll(glpath, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
238
store_test.go
238
store_test.go
|
|
@ -247,3 +247,241 @@ func TestStore(t *testing.T) {
|
|||
store.Free()
|
||||
store.Free()
|
||||
}
|
||||
|
||||
func TestWithSplitStore(t *testing.T) {
|
||||
wd := t.TempDir()
|
||||
|
||||
pullOpts := map[string]string{"Test1": "test1", "Test2": "test2"}
|
||||
store, err := GetStore(StoreOptions{
|
||||
RunRoot: filepath.Join(wd, "run"),
|
||||
GraphRoot: filepath.Join(wd, "root"),
|
||||
ImageStore: filepath.Join(wd, "imgstore"),
|
||||
GraphDriverName: "vfs",
|
||||
GraphDriverOptions: []string{},
|
||||
UIDMap: []idtools.IDMap{{
|
||||
ContainerID: 0,
|
||||
HostID: os.Getuid(),
|
||||
Size: 1,
|
||||
}},
|
||||
GIDMap: []idtools.IDMap{{
|
||||
ContainerID: 0,
|
||||
HostID: os.Getgid(),
|
||||
Size: 1,
|
||||
}},
|
||||
PullOptions: pullOpts,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
root := store.RunRoot()
|
||||
require.NotNil(t, root)
|
||||
|
||||
root = store.GraphRoot()
|
||||
require.NotNil(t, root)
|
||||
|
||||
root = store.GraphDriverName()
|
||||
require.NotNil(t, root)
|
||||
|
||||
gopts := store.GraphOptions()
|
||||
assert.Equal(t, []string{}, gopts)
|
||||
|
||||
store.UIDMap()
|
||||
store.GIDMap()
|
||||
|
||||
opts := store.PullOptions()
|
||||
assert.Equal(t, pullOpts, opts)
|
||||
|
||||
_, err = store.GraphDriver()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.CreateLayer("foo", "bar", nil, "", false, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, _, err = store.PutLayer("foo", "bar", nil, "", true, nil, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.CreateImage("foo", nil, "bar", "", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.CreateContainer("foo", nil, "bar", "layer", "", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Metadata("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetMetadata("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
exists := store.Exists("foobar")
|
||||
require.False(t, exists)
|
||||
|
||||
_, err = store.Status()
|
||||
require.Nil(t, err)
|
||||
|
||||
err = store.Delete("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.DeleteLayer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.DeleteImage("foobar", true)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.DeleteImage("foobar", false)
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.DeleteContainer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.DeleteContainer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.Wipe()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Mount("foobar", "")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Unmount("foobar", true)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Unmount("foobar", false)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Mounted("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Changes("foobar", "foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.DiffSize("foobar", "foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Diff("foobar", "foobar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ApplyDiff("foobar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
var d digest.Digest
|
||||
_, err = store.LayersByCompressedDigest(d)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.LayersByUncompressedDigest(d)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.LayerSize("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, _, err = store.LayerParentOwners("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Layers()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Images()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Containers()
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Names("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetNames("foobar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ListImageBigData("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ImageBigData("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ImageBigDataSize("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ImageBigDataDigest("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetImageBigData("foo", "bar", nil, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ImageSize("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ListContainerBigData("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerBigData("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerBigDataSize("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerBigDataDigest("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetContainerBigData("foo", "bar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerSize("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Layer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Image("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ImagesByTopLayer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
images, err := store.ImagesByDigest("foobar")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(images), 0)
|
||||
|
||||
_, err = store.Container("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerByLayer("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerDirectory("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetContainerDirectoryFile("foo", "bar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.FromContainerDirectory("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.ContainerRunDirectory("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
err = store.SetContainerRunDirectoryFile("foo", "bar", nil)
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.FromContainerRunDirectory("foo", "bar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, _, err = store.ContainerParentOwners("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Lookup("foobar")
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = store.Shutdown(false)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Shutdown(true)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = store.Version()
|
||||
require.Nil(t, err)
|
||||
|
||||
// GetDigestLock returns digest-specific Locker.
|
||||
_, err = store.GetDigestLock(d)
|
||||
require.Error(t, err)
|
||||
|
||||
store.Free()
|
||||
store.Free()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
@test "split-store" {
|
||||
# Create and populate three interesting layers.
|
||||
populate
|
||||
|
||||
# Create an image using to top layer.
|
||||
name=wonderful-image
|
||||
run mkdir -p ${TESTDIR}/imagestore
|
||||
run mkdir -p ${TESTDIR}/emptyimagestore
|
||||
run storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ --debug=false create-image --name $name
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" != "" ]
|
||||
image=${lines[0]}
|
||||
|
||||
# Add a couple of big data items.
|
||||
createrandom ${TESTDIR}/random1
|
||||
createrandom ${TESTDIR}/random2
|
||||
storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ set-image-data -f ${TESTDIR}/random1 $image random1
|
||||
storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ set-image-data -f ${TESTDIR}/random2 $image random2
|
||||
|
||||
# Get information about the image, and make sure the ID, name, and data names were preserved.
|
||||
run storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ image $image
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "ID: $image" ]]
|
||||
[[ "$output" =~ "Name: $name" ]]
|
||||
[[ "$output" =~ "Data: random1" ]]
|
||||
[[ "$output" =~ "Data: random2" ]]
|
||||
|
||||
# shutdown store
|
||||
run storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ shutdown
|
||||
|
||||
# Similar data must not be shown when image-store is switched to empty store
|
||||
run storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/emptyimagestore/ --run ${TESTDIR}/runroot/ image $image
|
||||
echo "$output"
|
||||
[[ "$output" != "ID: $image" ]]
|
||||
[[ "$output" != "Name: $name" ]]
|
||||
[[ "$output" != "Data: random1" ]]
|
||||
[[ "$output" != "Data: random2" ]]
|
||||
|
||||
# shutdown store
|
||||
run storage --graph ${TESTDIR}/graph/ --image-store ${TESTDIR}/emptyimagestore/ --run ${TESTDIR}/runroot/ shutdown
|
||||
}
|
||||
|
||||
@test "split-store - use graphRoot as an additional store by default" {
|
||||
case "$STORAGE_DRIVER" in
|
||||
overlay*)
|
||||
;;
|
||||
*)
|
||||
skip "additional store not supported by driver $STORAGE_DRIVER"
|
||||
;;
|
||||
esac
|
||||
# Create and populate three interesting layers.
|
||||
populate
|
||||
|
||||
# Create an image using to top layer.
|
||||
name=wonderful-image
|
||||
run mkdir -p ${TESTDIR}/imagestore
|
||||
run storage --graph ${TESTDIR}/graph --debug=false create-image --name $name
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$output" != "" ]
|
||||
image=${lines[0]}
|
||||
|
||||
# Add a couple of big data items.
|
||||
createrandom ${TESTDIR}/random1
|
||||
createrandom ${TESTDIR}/random2
|
||||
storage --graph ${TESTDIR}/graph set-image-data -f ${TESTDIR}/random1 $image random1
|
||||
storage --graph ${TESTDIR}/graph set-image-data -f ${TESTDIR}/random2 $image random2
|
||||
|
||||
# Get information about the image, and make sure the ID, name, and data names were preserved.
|
||||
run storage --graph ${TESTDIR}/graph image $image
|
||||
echo "$output"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "ID: $image" ]]
|
||||
[[ "$output" =~ "Name: $name" ]]
|
||||
[[ "$output" =~ "Data: random1" ]]
|
||||
[[ "$output" =~ "Data: random2" ]]
|
||||
|
||||
# shutdown store
|
||||
run storage --graph ${TESTDIR}/graph shutdown
|
||||
|
||||
# Similar data must not be shown when image-store is switched to empty store
|
||||
run storage --graph ${TESTDIR}/graph --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ image $image
|
||||
echo "$output"
|
||||
[[ "$output" =~ "ID: $image" ]]
|
||||
[[ "$output" =~ "Name: $name" ]]
|
||||
[[ "$output" =~ "Data: random1" ]]
|
||||
[[ "$output" =~ "Data: random2" ]]
|
||||
# Since this image is being read from the readonly graph root
|
||||
# so it must show that
|
||||
[[ "$output" =~ "Read Only: true" ]]
|
||||
|
||||
# shutdown store
|
||||
run storage --graph ${TESTDIR}/graph --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ shutdown
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ type TomlConfig struct {
|
|||
Driver string `toml:"driver,omitempty"`
|
||||
DriverPriority []string `toml:"driver_priority,omitempty"`
|
||||
RunRoot string `toml:"runroot,omitempty"`
|
||||
ImageStore string `toml:"imagestore,omitempty"`
|
||||
GraphRoot string `toml:"graphroot,omitempty"`
|
||||
RootlessStoragePath string `toml:"rootless_storage_path,omitempty"`
|
||||
TransientStore bool `toml:"transient_store,omitempty"`
|
||||
|
|
@ -215,6 +216,10 @@ type StoreOptions struct {
|
|||
// GraphRoot is the filesystem path under which we will store the
|
||||
// contents of layers, images, and containers.
|
||||
GraphRoot string `json:"root,omitempty"`
|
||||
// Image Store is the location of image store which is seperated from the
|
||||
// container store. Usually this is not recommended unless users wants
|
||||
// seperate store for image and containers.
|
||||
ImageStore string `json:"imagestore,omitempty"`
|
||||
// RootlessStoragePath is the storage path for rootless users
|
||||
// default $HOME/.local/share/containers/storage
|
||||
RootlessStoragePath string `toml:"rootless_storage_path"`
|
||||
|
|
@ -405,6 +410,9 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro
|
|||
if config.Storage.GraphRoot != "" {
|
||||
storeOptions.GraphRoot = config.Storage.GraphRoot
|
||||
}
|
||||
if config.Storage.ImageStore != "" {
|
||||
storeOptions.ImageStore = config.Storage.ImageStore
|
||||
}
|
||||
if config.Storage.RootlessStoragePath != "" {
|
||||
storeOptions.RootlessStoragePath = config.Storage.RootlessStoragePath
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue