Add read-only layer/image/container stores
Implement read-only versions of layer and image store interfaces which allocate read-only locks and which return errors whenever a write function is called (which should only be possible after a type assertion, since they're not part of the read-only interfaces). Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
198f752fb5
commit
a9b1fe6241
58
images.go
58
images.go
|
|
@ -2,7 +2,6 @@ package storage
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -11,6 +10,7 @@ import (
|
|||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -123,7 +123,7 @@ func (r *imageStore) datapath(id, key string) string {
|
|||
}
|
||||
|
||||
func (r *imageStore) Load() error {
|
||||
needSave := false
|
||||
shouldSave := false
|
||||
rpath := r.imagespath()
|
||||
data, err := ioutil.ReadFile(rpath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
|
|
@ -140,23 +140,29 @@ func (r *imageStore) Load() error {
|
|||
for _, name := range image.Names {
|
||||
if conflict, ok := names[name]; ok {
|
||||
r.removeName(conflict, name)
|
||||
needSave = true
|
||||
shouldSave = true
|
||||
}
|
||||
names[name] = images[n]
|
||||
}
|
||||
}
|
||||
}
|
||||
if shouldSave && !r.IsReadWrite() {
|
||||
return errors.New("image store assigns the same name to multiple images")
|
||||
}
|
||||
r.images = images
|
||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||
r.byid = ids
|
||||
r.byname = names
|
||||
if needSave {
|
||||
if shouldSave {
|
||||
return r.Save()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *imageStore) Save() error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the image store at %q", r.imagespath())
|
||||
}
|
||||
rpath := r.imagespath()
|
||||
if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
|
||||
return err
|
||||
|
|
@ -192,6 +198,26 @@ func newImageStore(dir string) (ImageStore, error) {
|
|||
return &istore, nil
|
||||
}
|
||||
|
||||
func newROImageStore(dir string) (ROImageStore, error) {
|
||||
lockfile, err := GetROLockfile(filepath.Join(dir, "images.lock"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockfile.Lock()
|
||||
defer lockfile.Unlock()
|
||||
istore := imageStore{
|
||||
lockfile: lockfile,
|
||||
dir: dir,
|
||||
images: []*Image{},
|
||||
byid: make(map[string]*Image),
|
||||
byname: make(map[string]*Image),
|
||||
}
|
||||
if err := istore.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &istore, nil
|
||||
}
|
||||
|
||||
func (r *imageStore) lookup(id string) (*Image, bool) {
|
||||
if image, ok := r.byid[id]; ok {
|
||||
return image, ok
|
||||
|
|
@ -205,6 +231,9 @@ func (r *imageStore) lookup(id string) (*Image, bool) {
|
|||
}
|
||||
|
||||
func (r *imageStore) ClearFlag(id string, flag string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on images at %q", r.imagespath())
|
||||
}
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
|
|
@ -214,6 +243,9 @@ func (r *imageStore) ClearFlag(id string, flag string) error {
|
|||
}
|
||||
|
||||
func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on images at %q", r.imagespath())
|
||||
}
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
|
|
@ -223,6 +255,9 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
|
|||
}
|
||||
|
||||
func (r *imageStore) Create(id string, names []string, layer, metadata string) (image *Image, err error) {
|
||||
if !r.IsReadWrite() {
|
||||
return nil, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new images at %q", r.imagespath())
|
||||
}
|
||||
if id == "" {
|
||||
id = stringid.GenerateRandomID()
|
||||
_, idInUse := r.byid[id]
|
||||
|
|
@ -268,6 +303,9 @@ func (r *imageStore) Metadata(id string) (string, error) {
|
|||
}
|
||||
|
||||
func (r *imageStore) SetMetadata(id, metadata string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify image metadata at %q", r.imagespath())
|
||||
}
|
||||
if image, ok := r.lookup(id); ok {
|
||||
image.Metadata = metadata
|
||||
return r.Save()
|
||||
|
|
@ -280,6 +318,9 @@ func (r *imageStore) removeName(image *Image, name string) {
|
|||
}
|
||||
|
||||
func (r *imageStore) SetNames(id string, names []string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change image name assignments at %q", r.imagespath())
|
||||
}
|
||||
if image, ok := r.lookup(id); ok {
|
||||
for _, name := range image.Names {
|
||||
delete(r.byname, name)
|
||||
|
|
@ -297,6 +338,9 @@ func (r *imageStore) SetNames(id string, names []string) error {
|
|||
}
|
||||
|
||||
func (r *imageStore) Delete(id string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
|
||||
}
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
|
|
@ -370,6 +414,9 @@ func (r *imageStore) BigDataNames(id string) ([]string, error) {
|
|||
}
|
||||
|
||||
func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to save data items associated with images at %q", r.imagespath())
|
||||
}
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
|
|
@ -404,6 +451,9 @@ func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
|||
}
|
||||
|
||||
func (r *imageStore) Wipe() error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete images at %q", r.imagespath())
|
||||
}
|
||||
ids := []string{}
|
||||
for id := range r.byid {
|
||||
ids = append(ids, id)
|
||||
|
|
|
|||
89
layers.go
89
layers.go
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
|
@ -16,6 +15,7 @@ import (
|
|||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vbatts/tar-split/tar/asm"
|
||||
"github.com/vbatts/tar-split/tar/storage"
|
||||
)
|
||||
|
|
@ -192,7 +192,7 @@ func (r *layerStore) layerspath() string {
|
|||
}
|
||||
|
||||
func (r *layerStore) Load() error {
|
||||
needSave := false
|
||||
shouldSave := false
|
||||
rpath := r.layerspath()
|
||||
data, err := ioutil.ReadFile(rpath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
|
|
@ -211,7 +211,7 @@ func (r *layerStore) Load() error {
|
|||
for _, name := range layer.Names {
|
||||
if conflict, ok := names[name]; ok {
|
||||
r.removeName(conflict, name)
|
||||
needSave = true
|
||||
shouldSave = true
|
||||
}
|
||||
names[name] = layers[n]
|
||||
}
|
||||
|
|
@ -222,6 +222,9 @@ func (r *layerStore) Load() error {
|
|||
}
|
||||
}
|
||||
}
|
||||
if shouldSave && !r.IsReadWrite() {
|
||||
return errors.New("layer store assigns the same name to multiple layers")
|
||||
}
|
||||
mpath := r.mountspath()
|
||||
data, err = ioutil.ReadFile(mpath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
|
|
@ -245,27 +248,32 @@ func (r *layerStore) Load() error {
|
|||
r.byname = names
|
||||
r.bymount = mounts
|
||||
err = nil
|
||||
// Last step: try to remove anything that a previous user of this
|
||||
// storage area marked for deletion but didn't manage to actually
|
||||
// delete.
|
||||
for _, layer := range r.layers {
|
||||
if cleanup, ok := layer.Flags[incompleteFlag]; ok {
|
||||
if b, ok := cleanup.(bool); ok && b {
|
||||
err = r.Delete(layer.ID)
|
||||
if err != nil {
|
||||
break
|
||||
// Last step: if we're writable, try to remove anything that a previous
|
||||
// user of this storage area marked for deletion but didn't manage to
|
||||
// actually delete.
|
||||
if r.IsReadWrite() {
|
||||
for _, layer := range r.layers {
|
||||
if cleanup, ok := layer.Flags[incompleteFlag]; ok {
|
||||
if b, ok := cleanup.(bool); ok && b {
|
||||
err = r.Delete(layer.ID)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
shouldSave = true
|
||||
}
|
||||
needSave = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if needSave {
|
||||
return r.Save()
|
||||
if shouldSave {
|
||||
return r.Save()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *layerStore) Save() error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the layer store at %q", r.layerspath())
|
||||
}
|
||||
rpath := r.layerspath()
|
||||
if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
|
||||
return err
|
||||
|
|
@ -327,6 +335,28 @@ func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (Layer
|
|||
return &rlstore, nil
|
||||
}
|
||||
|
||||
func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (ROLayerStore, error) {
|
||||
lockfile, err := GetROLockfile(filepath.Join(layerdir, "layers.lock"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockfile.Lock()
|
||||
defer lockfile.Unlock()
|
||||
rlstore := layerStore{
|
||||
lockfile: lockfile,
|
||||
driver: driver,
|
||||
rundir: rundir,
|
||||
layerdir: layerdir,
|
||||
byid: make(map[string]*Layer),
|
||||
bymount: make(map[string]*Layer),
|
||||
byname: make(map[string]*Layer),
|
||||
}
|
||||
if err := rlstore.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rlstore, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) lookup(id string) (*Layer, bool) {
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
return layer, ok
|
||||
|
|
@ -340,6 +370,9 @@ func (r *layerStore) lookup(id string) (*Layer, bool) {
|
|||
}
|
||||
|
||||
func (r *layerStore) ClearFlag(id string, flag string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to clear flags on layers at %q", r.layerspath())
|
||||
}
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
|
|
@ -349,6 +382,9 @@ func (r *layerStore) ClearFlag(id string, flag string) error {
|
|||
}
|
||||
|
||||
func (r *layerStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to set flags on layers at %q", r.layerspath())
|
||||
}
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
|
|
@ -362,6 +398,9 @@ func (r *layerStore) Status() ([][2]string, error) {
|
|||
}
|
||||
|
||||
func (r *layerStore) Put(id, parent string, names []string, mountLabel string, options map[string]string, writeable bool, flags map[string]interface{}, diff archive.Reader) (layer *Layer, size int64, err error) {
|
||||
if !r.IsReadWrite() {
|
||||
return nil, -1, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to create new layers at %q", r.layerspath())
|
||||
}
|
||||
size = -1
|
||||
if err := os.MkdirAll(r.rundir, 0700); err != nil {
|
||||
return nil, -1, err
|
||||
|
|
@ -453,6 +492,9 @@ func (r *layerStore) Create(id, parent string, names []string, mountLabel string
|
|||
}
|
||||
|
||||
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
||||
if !r.IsReadWrite() {
|
||||
return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
||||
}
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return "", ErrLayerUnknown
|
||||
|
|
@ -478,6 +520,9 @@ func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
|||
}
|
||||
|
||||
func (r *layerStore) Unmount(id string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
||||
}
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
||||
|
|
@ -507,6 +552,9 @@ func (r *layerStore) removeName(layer *Layer, name string) {
|
|||
}
|
||||
|
||||
func (r *layerStore) SetNames(id string, names []string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to change layer name assignments at %q", r.layerspath())
|
||||
}
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
|
|
@ -531,6 +579,9 @@ func (r *layerStore) Metadata(id string) (string, error) {
|
|||
}
|
||||
|
||||
func (r *layerStore) SetMetadata(id, metadata string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify layer metadata at %q", r.layerspath())
|
||||
}
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
layer.Metadata = metadata
|
||||
return r.Save()
|
||||
|
|
@ -543,6 +594,9 @@ func (r *layerStore) tspath(id string) string {
|
|||
}
|
||||
|
||||
func (r *layerStore) Delete(id string) error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath())
|
||||
}
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
|
|
@ -595,6 +649,9 @@ func (r *layerStore) Get(id string) (*Layer, error) {
|
|||
}
|
||||
|
||||
func (r *layerStore) Wipe() error {
|
||||
if !r.IsReadWrite() {
|
||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath())
|
||||
}
|
||||
ids := []string{}
|
||||
for id := range r.byid {
|
||||
ids = append(ids, id)
|
||||
|
|
|
|||
2
store.go
2
store.go
|
|
@ -52,6 +52,8 @@ var (
|
|||
ErrIncompleteOptions = errors.New("missing necessary StoreOptions")
|
||||
// ErrSizeUnknown is returned when the caller asks for the size of a big data item, but the Store couldn't determine the answer.
|
||||
ErrSizeUnknown = errors.New("size is not known")
|
||||
// ErrStoreIsReadOnly is returned when the caller makes a call to a read-only store that would require modifying its contents.
|
||||
ErrStoreIsReadOnly = errors.New("called a write method on a read-only store")
|
||||
// DefaultStoreOptions is a reasonable default set of options.
|
||||
DefaultStoreOptions StoreOptions
|
||||
stores []*store
|
||||
|
|
|
|||
Loading…
Reference in New Issue