Merge pull request #663 from dmcgowan/snapshot-storage
Refactor snapshot storage package
This commit is contained in:
commit
2048f891d2
12 changed files with 422 additions and 432 deletions
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/containerd/containerd/snapshot/storage/boltdb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stevvooe/go-btrfs"
|
||||
)
|
||||
|
@ -32,24 +31,21 @@ func init() {
|
|||
// TODO: check device for root
|
||||
return nil, errors.Errorf("btrfs requires \"device\" configuration")
|
||||
}
|
||||
|
||||
ms, err := boltdb.NewMetaStore(ic.Context, filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSnapshotter(conf.Device, root, ms)
|
||||
return NewSnapshotter(conf.Device, root)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type Snapshotter struct {
|
||||
type snapshotter struct {
|
||||
device string // maybe we can resolve it with path?
|
||||
root string // root provides paths for internal storage.
|
||||
ms storage.MetaStore
|
||||
ms *storage.MetaStore
|
||||
}
|
||||
|
||||
func NewSnapshotter(device, root string, ms storage.MetaStore) (snapshot.Snapshotter, error) {
|
||||
// NewSnapshotter returns a Snapshotter using btrfs. Uses the provided
|
||||
// device and root directory for snapshots and stores the metadata in
|
||||
// a file in the provided root.
|
||||
func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) {
|
||||
var (
|
||||
active = filepath.Join(root, "active")
|
||||
snapshots = filepath.Join(root, "snapshots")
|
||||
|
@ -63,8 +59,12 @@ func NewSnapshotter(device, root string, ms storage.MetaStore) (snapshot.Snapsho
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Snapshotter{
|
||||
return &snapshotter{
|
||||
device: device,
|
||||
root: root,
|
||||
ms: ms,
|
||||
|
@ -76,34 +76,34 @@ func NewSnapshotter(device, root string, ms storage.MetaStore) (snapshot.Snapsho
|
|||
//
|
||||
// Should be used for parent resolution, existence checks and to discern
|
||||
// the kind of snapshot.
|
||||
func (b *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
func (b *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return snapshot.Info{}, err
|
||||
}
|
||||
defer t.Rollback()
|
||||
return b.ms.Stat(ctx, key)
|
||||
return storage.GetInfo(ctx, key)
|
||||
}
|
||||
|
||||
// Walk the committed snapshots.
|
||||
func (b *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
func (b *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.Rollback()
|
||||
return b.ms.Walk(ctx, fn)
|
||||
return storage.WalkInfo(ctx, fn)
|
||||
}
|
||||
|
||||
func (b *Snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
func (b *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
return b.makeActive(ctx, key, parent, false)
|
||||
}
|
||||
|
||||
func (b *Snapshotter) View(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
func (b *snapshotter) View(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
return b.makeActive(ctx, key, parent, true)
|
||||
}
|
||||
|
||||
func (b *Snapshotter) makeActive(ctx context.Context, key, parent string, readonly bool) ([]containerd.Mount, error) {
|
||||
func (b *snapshotter) makeActive(ctx context.Context, key, parent string, readonly bool) ([]containerd.Mount, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -116,7 +116,7 @@ func (b *Snapshotter) makeActive(ctx context.Context, key, parent string, readon
|
|||
}
|
||||
}()
|
||||
|
||||
a, err := b.ms.CreateActive(ctx, key, parent, readonly)
|
||||
a, err := storage.CreateActive(ctx, key, parent, readonly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func (b *Snapshotter) makeActive(ctx context.Context, key, parent string, readon
|
|||
return b.mounts(target)
|
||||
}
|
||||
|
||||
func (b *Snapshotter) mounts(dir string) ([]containerd.Mount, error) {
|
||||
func (b *snapshotter) mounts(dir string) ([]containerd.Mount, error) {
|
||||
var options []string
|
||||
|
||||
// get the subvolume id back out for the mount
|
||||
|
@ -174,7 +174,7 @@ func (b *Snapshotter) mounts(dir string) ([]containerd.Mount, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (b *Snapshotter) Commit(ctx context.Context, name, key string) (err error) {
|
||||
func (b *snapshotter) Commit(ctx context.Context, name, key string) (err error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -187,7 +187,7 @@ func (b *Snapshotter) Commit(ctx context.Context, name, key string) (err error)
|
|||
}
|
||||
}()
|
||||
|
||||
id, err := b.ms.Commit(ctx, key, name)
|
||||
id, err := storage.CommitActive(ctx, key, name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to commit")
|
||||
}
|
||||
|
@ -220,12 +220,12 @@ func (b *Snapshotter) Commit(ctx context.Context, name, key string) (err error)
|
|||
// called on an read-write or readonly transaction.
|
||||
//
|
||||
// This can be used to recover mounts after calling View or Prepare.
|
||||
func (b *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Mount, error) {
|
||||
func (b *snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Mount, error) {
|
||||
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, err := b.ms.GetActive(ctx, key)
|
||||
a, err := storage.GetActive(ctx, key)
|
||||
t.Rollback()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get active snapshot")
|
||||
|
@ -236,7 +236,7 @@ func (b *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Moun
|
|||
|
||||
// Remove abandons the transaction identified by key. All resources
|
||||
// associated with the key will be removed.
|
||||
func (b *Snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
func (b *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
var (
|
||||
source, removed string
|
||||
readonly bool
|
||||
|
@ -260,7 +260,7 @@ func (b *Snapshotter) Remove(ctx context.Context, key string) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
id, k, err := b.ms.Remove(ctx, key)
|
||||
id, k, err := storage.Remove(ctx, key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to remove snapshot")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage/boltdb"
|
||||
"github.com/containerd/containerd/snapshot/testsuite"
|
||||
"github.com/containerd/containerd/testutil"
|
||||
)
|
||||
|
@ -23,11 +22,7 @@ const (
|
|||
func boltSnapshotter(t *testing.T) func(context.Context, string) (snapshot.Snapshotter, func(), error) {
|
||||
return func(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
||||
device := setupBtrfsLoopbackDevice(t, root)
|
||||
store, err := boltdb.NewMetaStore(ctx, filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
snapshotter, err := NewSnapshotter(device.deviceName, root, store)
|
||||
snapshotter, err := NewSnapshotter(device.deviceName, root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/containerd/containerd/snapshot/storage/boltdb"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -21,19 +20,14 @@ func init() {
|
|||
plugin.Register("snapshot-overlay", &plugin.Registration{
|
||||
Type: plugin.SnapshotPlugin,
|
||||
Init: func(ic *plugin.InitContext) (interface{}, error) {
|
||||
root := filepath.Join(ic.Root, "snapshot", "overlay")
|
||||
ms, err := boltdb.NewMetaStore(ic.Context, filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSnapshotter(root, ms)
|
||||
return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "overlay"))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type Snapshotter struct {
|
||||
type snapshotter struct {
|
||||
root string
|
||||
ms storage.MetaStore
|
||||
ms *storage.MetaStore
|
||||
}
|
||||
|
||||
type activeSnapshot struct {
|
||||
|
@ -43,15 +37,23 @@ type activeSnapshot struct {
|
|||
readonly bool
|
||||
}
|
||||
|
||||
func NewSnapshotter(root string, ms storage.MetaStore) (snapshot.Snapshotter, error) {
|
||||
// NewSnapshotter returns a Snapshotter which uses overlayfs. The overlayfs
|
||||
// diffs are stored under the provided root. A metadata file is stored under
|
||||
// the root.
|
||||
func NewSnapshotter(root string) (snapshot.Snapshotter, error) {
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(root, "snapshots"), 0700); err != nil {
|
||||
ms, err := storage.NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Snapshotter{
|
||||
if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &snapshotter{
|
||||
root: root,
|
||||
ms: ms,
|
||||
}, nil
|
||||
|
@ -62,20 +64,20 @@ func NewSnapshotter(root string, ms storage.MetaStore) (snapshot.Snapshotter, er
|
|||
//
|
||||
// Should be used for parent resolution, existence checks and to discern
|
||||
// the kind of snapshot.
|
||||
func (o *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
func (o *snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return snapshot.Info{}, err
|
||||
}
|
||||
defer t.Rollback()
|
||||
return o.ms.Stat(ctx, key)
|
||||
return storage.GetInfo(ctx, key)
|
||||
}
|
||||
|
||||
func (o *Snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
func (o *snapshotter) Prepare(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
return o.createActive(ctx, key, parent, false)
|
||||
}
|
||||
|
||||
func (o *Snapshotter) View(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
func (o *snapshotter) View(ctx context.Context, key, parent string) ([]containerd.Mount, error) {
|
||||
return o.createActive(ctx, key, parent, true)
|
||||
}
|
||||
|
||||
|
@ -83,12 +85,12 @@ func (o *Snapshotter) View(ctx context.Context, key, parent string) ([]container
|
|||
// called on an read-write or readonly transaction.
|
||||
//
|
||||
// This can be used to recover mounts after calling View or Prepare.
|
||||
func (o *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Mount, error) {
|
||||
func (o *snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Mount, error) {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
active, err := o.ms.GetActive(ctx, key)
|
||||
active, err := storage.GetActive(ctx, key)
|
||||
t.Rollback()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get active mount")
|
||||
|
@ -96,12 +98,12 @@ func (o *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Moun
|
|||
return o.mounts(active), nil
|
||||
}
|
||||
|
||||
func (o *Snapshotter) Commit(ctx context.Context, name, key string) error {
|
||||
func (o *snapshotter) Commit(ctx context.Context, name, key string) error {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := o.ms.Commit(ctx, key, name); err != nil {
|
||||
if _, err := storage.CommitActive(ctx, key, name); err != nil {
|
||||
if rerr := t.Rollback(); rerr != nil {
|
||||
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||
}
|
||||
|
@ -112,7 +114,7 @@ func (o *Snapshotter) Commit(ctx context.Context, name, key string) error {
|
|||
|
||||
// Remove abandons the transaction identified by key. All resources
|
||||
// associated with the key will be removed.
|
||||
func (o *Snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
func (o *snapshotter) Remove(ctx context.Context, key string) (err error) {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -125,7 +127,7 @@ func (o *Snapshotter) Remove(ctx context.Context, key string) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
id, _, err := o.ms.Remove(ctx, key)
|
||||
id, _, err := storage.Remove(ctx, key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to remove")
|
||||
}
|
||||
|
@ -154,16 +156,16 @@ func (o *Snapshotter) Remove(ctx context.Context, key string) (err error) {
|
|||
}
|
||||
|
||||
// Walk the committed snapshots.
|
||||
func (o *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
func (o *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer t.Rollback()
|
||||
return o.ms.Walk(ctx, fn)
|
||||
return storage.WalkInfo(ctx, fn)
|
||||
}
|
||||
|
||||
func (o *Snapshotter) createActive(ctx context.Context, key, parent string, readonly bool) ([]containerd.Mount, error) {
|
||||
func (o *snapshotter) createActive(ctx context.Context, key, parent string, readonly bool) ([]containerd.Mount, error) {
|
||||
var (
|
||||
path string
|
||||
snapshotDir = filepath.Join(o.root, "snapshots")
|
||||
|
@ -202,7 +204,7 @@ func (o *Snapshotter) createActive(ctx context.Context, key, parent string, read
|
|||
return nil, err
|
||||
}
|
||||
|
||||
active, err := o.ms.CreateActive(ctx, key, parent, readonly)
|
||||
active, err := storage.CreateActive(ctx, key, parent, readonly)
|
||||
if err != nil {
|
||||
if rerr := t.Rollback(); rerr != nil {
|
||||
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||
|
@ -211,7 +213,7 @@ func (o *Snapshotter) createActive(ctx context.Context, key, parent string, read
|
|||
}
|
||||
|
||||
path = filepath.Join(snapshotDir, active.ID)
|
||||
if err := os.Rename(td, path); err != nil {
|
||||
if err = os.Rename(td, path); err != nil {
|
||||
if rerr := t.Rollback(); rerr != nil {
|
||||
log.G(ctx).WithError(rerr).Warn("Failure rolling back transaction")
|
||||
}
|
||||
|
@ -219,14 +221,14 @@ func (o *Snapshotter) createActive(ctx context.Context, key, parent string, read
|
|||
}
|
||||
td = ""
|
||||
|
||||
if err := t.Commit(); err != nil {
|
||||
if err = t.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit failed")
|
||||
}
|
||||
|
||||
return o.mounts(active), nil
|
||||
}
|
||||
|
||||
func (o *Snapshotter) mounts(active storage.Active) []containerd.Mount {
|
||||
func (o *snapshotter) mounts(active storage.Active) []containerd.Mount {
|
||||
if len(active.ParentIDs) == 0 {
|
||||
// if we only have one layer/no parents then just return a bind mount as overlay
|
||||
// will not work
|
||||
|
@ -282,10 +284,10 @@ func (o *Snapshotter) mounts(active storage.Active) []containerd.Mount {
|
|||
|
||||
}
|
||||
|
||||
func (o *Snapshotter) upperPath(id string) string {
|
||||
func (o *snapshotter) upperPath(id string) string {
|
||||
return filepath.Join(o.root, "snapshots", id, "fs")
|
||||
}
|
||||
|
||||
func (o *Snapshotter) workPath(id string) string {
|
||||
func (o *snapshotter) workPath(id string) string {
|
||||
return filepath.Join(o.root, "snapshots", id, "work")
|
||||
}
|
||||
|
|
|
@ -11,17 +11,13 @@ import (
|
|||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage/boltdb"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/containerd/containerd/snapshot/testsuite"
|
||||
"github.com/containerd/containerd/testutil"
|
||||
)
|
||||
|
||||
func boltSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
||||
store, err := boltdb.NewMetaStore(ctx, filepath.Join(root, "metadata.db"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
snapshotter, err := NewSnapshotter(root, store)
|
||||
func newSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
||||
snapshotter, err := NewSnapshotter(root)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -31,7 +27,7 @@ func boltSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, fu
|
|||
|
||||
func TestOverlay(t *testing.T) {
|
||||
testutil.RequiresRoot(t)
|
||||
testsuite.SnapshotterSuite(t, "Overlay", boltSnapshotter)
|
||||
testsuite.SnapshotterSuite(t, "Overlay", newSnapshotter)
|
||||
}
|
||||
|
||||
func TestOverlayMounts(t *testing.T) {
|
||||
|
@ -41,7 +37,7 @@ func TestOverlayMounts(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
o, _, err := boltSnapshotter(ctx, root)
|
||||
o, _, err := newSnapshotter(ctx, root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -77,7 +73,7 @@ func TestOverlayCommit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
o, _, err := boltSnapshotter(ctx, root)
|
||||
o, _, err := newSnapshotter(ctx, root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -106,7 +102,7 @@ func TestOverlayOverlayMount(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
o, _, err := boltSnapshotter(ctx, root)
|
||||
o, _, err := newSnapshotter(ctx, root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -153,14 +149,14 @@ func TestOverlayOverlayMount(t *testing.T) {
|
|||
}
|
||||
|
||||
func getBasePath(ctx context.Context, sn snapshot.Snapshotter, root, key string) string {
|
||||
o := sn.(*Snapshotter)
|
||||
o := sn.(*snapshotter)
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer t.Rollback()
|
||||
|
||||
active, err := o.ms.GetActive(ctx, key)
|
||||
active, err := storage.GetActive(ctx, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -169,13 +165,13 @@ func getBasePath(ctx context.Context, sn snapshot.Snapshotter, root, key string)
|
|||
}
|
||||
|
||||
func getParents(ctx context.Context, sn snapshot.Snapshotter, root, key string) []string {
|
||||
o := sn.(*Snapshotter)
|
||||
o := sn.(*snapshotter)
|
||||
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer t.Rollback()
|
||||
active, err := o.ms.GetActive(ctx, key)
|
||||
active, err := storage.GetActive(ctx, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -194,7 +190,7 @@ func TestOverlayOverlayRead(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
o, _, err := boltSnapshotter(ctx, root)
|
||||
o, _, err := newSnapshotter(ctx, root)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -246,7 +242,7 @@ func TestOverlayView(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
o, _, err := boltSnapshotter(ctx, root)
|
||||
o, _, err := newSnapshotter(ctx, root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package boltdb
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
db "github.com/containerd/containerd/snapshot/storage/proto"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -16,6 +16,10 @@ var (
|
|||
bucketKeyStorageVersion = []byte("v1")
|
||||
bucketKeySnapshot = []byte("snapshots")
|
||||
bucketKeyParents = []byte("parents")
|
||||
|
||||
// ErrNoTransaction is returned when an operation is attempted with
|
||||
// a context which is not inside of a transaction.
|
||||
ErrNoTransaction = errors.New("no transaction in context")
|
||||
)
|
||||
|
||||
type boltFileTransactor struct {
|
||||
|
@ -23,91 +27,14 @@ type boltFileTransactor struct {
|
|||
tx *bolt.Tx
|
||||
}
|
||||
|
||||
type boltMetastore struct {
|
||||
dbfile string
|
||||
func (bft *boltFileTransactor) Rollback() error {
|
||||
defer bft.db.Close()
|
||||
return bft.tx.Rollback()
|
||||
}
|
||||
|
||||
// NewMetaStore returns a snapshot MetaStore for storage of metadata related to
|
||||
// a snapshot driver backed by a bolt file database. This implementation is
|
||||
// strongly consistent and does all metadata changes in a transaction to prevent
|
||||
// against process crashes causing inconsistent metadata state.
|
||||
func NewMetaStore(ctx context.Context, dbfile string) (storage.MetaStore, error) {
|
||||
return &boltMetastore{
|
||||
dbfile: dbfile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ms *boltFileTransactor) Rollback() error {
|
||||
defer ms.db.Close()
|
||||
return ms.tx.Rollback()
|
||||
}
|
||||
|
||||
func (ms *boltFileTransactor) Commit() error {
|
||||
defer ms.db.Close()
|
||||
return ms.tx.Commit()
|
||||
}
|
||||
|
||||
type transactionKey struct{}
|
||||
|
||||
func (ms *boltMetastore) TransactionContext(ctx context.Context, writable bool) (context.Context, storage.Transactor, error) {
|
||||
db, err := bolt.Open(ms.dbfile, 0600, nil)
|
||||
if err != nil {
|
||||
return ctx, nil, errors.Wrap(err, "failed to open database file")
|
||||
}
|
||||
|
||||
tx, err := db.Begin(writable)
|
||||
if err != nil {
|
||||
return ctx, nil, errors.Wrap(err, "failed to start transaction")
|
||||
}
|
||||
|
||||
t := &boltFileTransactor{
|
||||
db: db,
|
||||
tx: tx,
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, transactionKey{}, t)
|
||||
|
||||
return ctx, t, nil
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
|
||||
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
|
||||
if !ok {
|
||||
return errors.Errorf("no transaction in context")
|
||||
}
|
||||
bkt := t.tx.Bucket(bucketKeyStorageVersion)
|
||||
if bkt == nil {
|
||||
return errors.Wrap(snapshot.ErrSnapshotNotExist, "bucket does not exist")
|
||||
}
|
||||
return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents))
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
|
||||
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
|
||||
if !ok {
|
||||
return errors.Errorf("no transaction in context")
|
||||
}
|
||||
|
||||
bkt, err := t.tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create version bucket")
|
||||
}
|
||||
sbkt, err := bkt.CreateBucketIfNotExists(bucketKeySnapshot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create snapshots bucket")
|
||||
}
|
||||
pbkt, err := bkt.CreateBucketIfNotExists(bucketKeyParents)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create snapshots bucket")
|
||||
}
|
||||
return fn(ctx, sbkt, pbkt)
|
||||
}
|
||||
|
||||
func fromProtoKind(k Kind) snapshot.Kind {
|
||||
if k == KindActive {
|
||||
return snapshot.KindActive
|
||||
}
|
||||
return snapshot.KindCommitted
|
||||
func (bft *boltFileTransactor) Commit() error {
|
||||
defer bft.db.Close()
|
||||
return bft.tx.Commit()
|
||||
}
|
||||
|
||||
// parentKey returns a composite key of the parent and child identifiers. The
|
||||
|
@ -134,9 +61,11 @@ func getParentPrefix(b []byte) uint64 {
|
|||
return parent
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) Stat(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
var ss Snapshot
|
||||
err := ms.withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
// GetInfo returns the snapshot Info directly from the metadata. Requires a
|
||||
// context with a storage transaction.
|
||||
func GetInfo(ctx context.Context, key string) (snapshot.Info, error) {
|
||||
var ss db.Snapshot
|
||||
err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
return getSnapshot(bkt, key, &ss)
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -151,14 +80,17 @@ func (ms *boltMetastore) Stat(ctx context.Context, key string) (snapshot.Info, e
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
return ms.withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
// WalkInfo iterates through all metadata Info for the stored snapshots and
|
||||
// calls the provided function for each. Requires a context with a storage
|
||||
// transaction.
|
||||
func WalkInfo(ctx context.Context, fn func(context.Context, snapshot.Info) error) error {
|
||||
return withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
return bkt.ForEach(func(k, v []byte) error {
|
||||
// skip nested buckets
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
var ss Snapshot
|
||||
var ss db.Snapshot
|
||||
if err := proto.Unmarshal(v, &ss); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal snapshot")
|
||||
}
|
||||
|
@ -174,18 +106,23 @@ func (ms *boltMetastore) Walk(ctx context.Context, fn func(context.Context, snap
|
|||
})
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) CreateActive(ctx context.Context, key, parent string, readonly bool) (a storage.Active, err error) {
|
||||
err = ms.createBucketIfNotExists(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
// CreateActive creates a new active snapshot transaction referenced by
|
||||
// the provided key. The new active snapshot will have the provided
|
||||
// parent. If the readonly option is given, the active snapshot will be
|
||||
// marked as readonly and can only be removed, and not committed. The
|
||||
// provided context must contain a writable transaction.
|
||||
func CreateActive(ctx context.Context, key, parent string, readonly bool) (a Active, err error) {
|
||||
err = createBucketIfNotExists(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
var (
|
||||
parentS *Snapshot
|
||||
parentS *db.Snapshot
|
||||
)
|
||||
if parent != "" {
|
||||
parentS = new(Snapshot)
|
||||
parentS = new(db.Snapshot)
|
||||
if err := getSnapshot(bkt, parent, parentS); err != nil {
|
||||
return errors.Wrap(err, "failed to get parent snapshot")
|
||||
}
|
||||
|
||||
if parentS.Kind != KindCommitted {
|
||||
if parentS.Kind != db.KindCommitted {
|
||||
return errors.Wrap(snapshot.ErrSnapshotNotCommitted, "parent is not committed snapshot")
|
||||
}
|
||||
}
|
||||
|
@ -199,10 +136,10 @@ func (ms *boltMetastore) CreateActive(ctx context.Context, key, parent string, r
|
|||
return errors.Wrap(err, "unable to get identifier")
|
||||
}
|
||||
|
||||
ss := Snapshot{
|
||||
ss := db.Snapshot{
|
||||
ID: id,
|
||||
Parent: parent,
|
||||
Kind: KindActive,
|
||||
Kind: db.KindActive,
|
||||
Readonly: readonly,
|
||||
}
|
||||
if err := putSnapshot(bkt, key, &ss); err != nil {
|
||||
|
@ -216,7 +153,7 @@ func (ms *boltMetastore) CreateActive(ctx context.Context, key, parent string, r
|
|||
return errors.Wrap(err, "failed to write parent link")
|
||||
}
|
||||
|
||||
a.ParentIDs, err = ms.parents(bkt, parentS)
|
||||
a.ParentIDs, err = parents(bkt, parentS)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get parent chain")
|
||||
}
|
||||
|
@ -228,24 +165,26 @@ func (ms *boltMetastore) CreateActive(ctx context.Context, key, parent string, r
|
|||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return storage.Active{}, err
|
||||
return Active{}, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) GetActive(ctx context.Context, key string) (a storage.Active, err error) {
|
||||
err = ms.withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
// GetActive returns the metadata for the active snapshot transaction referenced
|
||||
// by the given key. Requires a context with a storage transaction.
|
||||
func GetActive(ctx context.Context, key string) (a Active, err error) {
|
||||
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
b := bkt.Get([]byte(key))
|
||||
if len(b) == 0 {
|
||||
return snapshot.ErrSnapshotNotExist
|
||||
}
|
||||
|
||||
var ss Snapshot
|
||||
var ss db.Snapshot
|
||||
if err := proto.Unmarshal(b, &ss); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal snapshot")
|
||||
}
|
||||
if ss.Kind != KindActive {
|
||||
if ss.Kind != db.KindActive {
|
||||
return snapshot.ErrSnapshotNotActive
|
||||
}
|
||||
|
||||
|
@ -253,12 +192,12 @@ func (ms *boltMetastore) GetActive(ctx context.Context, key string) (a storage.A
|
|||
a.Readonly = ss.Readonly
|
||||
|
||||
if ss.Parent != "" {
|
||||
var parent Snapshot
|
||||
var parent db.Snapshot
|
||||
if err := getSnapshot(bkt, ss.Parent, &parent); err != nil {
|
||||
return errors.Wrap(err, "failed to get parent snapshot")
|
||||
}
|
||||
|
||||
a.ParentIDs, err = ms.parents(bkt, &parent)
|
||||
a.ParentIDs, err = parents(bkt, &parent)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get parent chain")
|
||||
}
|
||||
|
@ -266,33 +205,18 @@ func (ms *boltMetastore) GetActive(ctx context.Context, key string) (a storage.A
|
|||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return storage.Active{}, err
|
||||
return Active{}, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) parents(bkt *bolt.Bucket, parent *Snapshot) (parents []string, err error) {
|
||||
for {
|
||||
parents = append(parents, fmt.Sprintf("%d", parent.ID))
|
||||
|
||||
if parent.Parent == "" {
|
||||
return
|
||||
}
|
||||
|
||||
var ps Snapshot
|
||||
if err := getSnapshot(bkt, parent.Parent, &ps); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get parent snapshot")
|
||||
}
|
||||
parent = &ps
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) Remove(ctx context.Context, key string) (id string, k snapshot.Kind, err error) {
|
||||
err = ms.withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
var ss Snapshot
|
||||
// Remove removes a snapshot from the metastore. The string identifier for the
|
||||
// snapshot is returned as well as the kind. The provided context must contain a
|
||||
// writable transaction.
|
||||
func Remove(ctx context.Context, key string) (id string, k snapshot.Kind, err error) {
|
||||
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
var ss db.Snapshot
|
||||
b := bkt.Get([]byte(key))
|
||||
if len(b) == 0 {
|
||||
return snapshot.ErrSnapshotNotExist
|
||||
|
@ -309,7 +233,7 @@ func (ms *boltMetastore) Remove(ctx context.Context, key string) (id string, k s
|
|||
}
|
||||
|
||||
if ss.Parent != "" {
|
||||
var ps Snapshot
|
||||
var ps db.Snapshot
|
||||
if err := getSnapshot(bkt, ss.Parent, &ps); err != nil {
|
||||
return errors.Wrap(err, "failed to get parent snapshot")
|
||||
}
|
||||
|
@ -333,25 +257,31 @@ func (ms *boltMetastore) Remove(ctx context.Context, key string) (id string, k s
|
|||
return
|
||||
}
|
||||
|
||||
func (ms *boltMetastore) Commit(ctx context.Context, key, name string) (id string, err error) {
|
||||
err = ms.withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
// CommitActive renames the active snapshot transaction referenced by `key`
|
||||
// as a committed snapshot referenced by `Name`. The resulting snapshot will be
|
||||
// committed and readonly. The `key` reference will no longer be available for
|
||||
// lookup or removal. The returned string identifier for the committed snapshot
|
||||
// is the same identifier of the original active snapshot. The provided context
|
||||
// must contain a writable transaction.
|
||||
func CommitActive(ctx context.Context, key, name string) (id string, err error) {
|
||||
err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
|
||||
b := bkt.Get([]byte(name))
|
||||
if len(b) != 0 {
|
||||
return errors.Wrap(snapshot.ErrSnapshotExist, "committed name already exists")
|
||||
}
|
||||
|
||||
var ss Snapshot
|
||||
var ss db.Snapshot
|
||||
if err := getSnapshot(bkt, key, &ss); err != nil {
|
||||
return errors.Wrap(err, "failed to get active snapshot")
|
||||
}
|
||||
if ss.Kind != KindActive {
|
||||
if ss.Kind != db.KindActive {
|
||||
return snapshot.ErrSnapshotNotActive
|
||||
}
|
||||
if ss.Readonly {
|
||||
return errors.Errorf("active snapshot is readonly")
|
||||
}
|
||||
|
||||
ss.Kind = KindCommitted
|
||||
ss.Kind = db.KindCommitted
|
||||
ss.Readonly = true
|
||||
|
||||
if err := putSnapshot(bkt, name, &ss); err != nil {
|
||||
|
@ -372,7 +302,65 @@ func (ms *boltMetastore) Commit(ctx context.Context, key, name string) (id strin
|
|||
return
|
||||
}
|
||||
|
||||
func getSnapshot(bkt *bolt.Bucket, key string, ss *Snapshot) error {
|
||||
func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
|
||||
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
|
||||
if !ok {
|
||||
return ErrNoTransaction
|
||||
}
|
||||
bkt := t.tx.Bucket(bucketKeyStorageVersion)
|
||||
if bkt == nil {
|
||||
return errors.Wrap(snapshot.ErrSnapshotNotExist, "bucket does not exist")
|
||||
}
|
||||
return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents))
|
||||
}
|
||||
|
||||
func createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
|
||||
t, ok := ctx.Value(transactionKey{}).(*boltFileTransactor)
|
||||
if !ok {
|
||||
return ErrNoTransaction
|
||||
}
|
||||
|
||||
bkt, err := t.tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create version bucket")
|
||||
}
|
||||
sbkt, err := bkt.CreateBucketIfNotExists(bucketKeySnapshot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create snapshots bucket")
|
||||
}
|
||||
pbkt, err := bkt.CreateBucketIfNotExists(bucketKeyParents)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create snapshots bucket")
|
||||
}
|
||||
return fn(ctx, sbkt, pbkt)
|
||||
}
|
||||
|
||||
func fromProtoKind(k db.Kind) snapshot.Kind {
|
||||
if k == db.KindActive {
|
||||
return snapshot.KindActive
|
||||
}
|
||||
return snapshot.KindCommitted
|
||||
}
|
||||
|
||||
func parents(bkt *bolt.Bucket, parent *db.Snapshot) (parents []string, err error) {
|
||||
for {
|
||||
parents = append(parents, fmt.Sprintf("%d", parent.ID))
|
||||
|
||||
if parent.Parent == "" {
|
||||
return
|
||||
}
|
||||
|
||||
var ps db.Snapshot
|
||||
if err := getSnapshot(bkt, parent.Parent, &ps); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get parent snapshot")
|
||||
}
|
||||
parent = &ps
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getSnapshot(bkt *bolt.Bucket, key string, ss *db.Snapshot) error {
|
||||
b := bkt.Get([]byte(key))
|
||||
if len(b) == 0 {
|
||||
return snapshot.ErrSnapshotNotExist
|
||||
|
@ -383,7 +371,7 @@ func getSnapshot(bkt *bolt.Bucket, key string, ss *Snapshot) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func putSnapshot(bkt *bolt.Bucket, key string, ss *Snapshot) error {
|
||||
func putSnapshot(bkt *bolt.Bucket, key string, ss *db.Snapshot) error {
|
||||
b, err := proto.Marshal(ss)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to marshal snapshot")
|
21
snapshot/storage/bolt_test.go
Normal file
21
snapshot/storage/bolt_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
// Does not require root but flag must be defined for snapshot tests
|
||||
_ "github.com/containerd/containerd/testutil"
|
||||
)
|
||||
|
||||
func TestMetastore(t *testing.T) {
|
||||
MetaStoreSuite(t, "Metastore", func(root string) (*MetaStore, error) {
|
||||
return NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
Benchmarks(b, "BoltDBBench", func(root string) (*MetaStore, error) {
|
||||
return NewMetaStore(filepath.Join(root, "metadata.db"))
|
||||
})
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package boltdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/containerd/containerd/snapshot/storage/testsuite"
|
||||
|
||||
// Does not require root but flag must be defined for snapshot tests
|
||||
_ "github.com/containerd/containerd/testutil"
|
||||
)
|
||||
|
||||
func TestBoltDB(t *testing.T) {
|
||||
testsuite.MetaStoreSuite(t, "BoltDB", func(ctx context.Context, root string) (storage.MetaStore, error) {
|
||||
return NewMetaStore(ctx, filepath.Join(root, "metadata.db"))
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSuite(b *testing.B) {
|
||||
testsuite.Benchmarks(b, "BoltDBBench", func(ctx context.Context, root string) (storage.MetaStore, error) {
|
||||
return NewMetaStore(ctx, filepath.Join(root, "metadata.db"))
|
||||
})
|
||||
}
|
|
@ -1,59 +1,28 @@
|
|||
// Package storage provides a metadata storage implementation for snapshot
|
||||
// drivers. Drive implementations are responsible for starting and managing
|
||||
// transactions using the defined context creator. This storage package uses
|
||||
// BoltDB for storing metadata. Access to the raw boltdb transaction is not
|
||||
// provided, but the stored object is provided by the proto subpackage.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MetaStore is used to store metadata related to a snapshot driver. The
|
||||
// MetaStore is intended to store metadata related to name, state and
|
||||
// parentage. Using the MetaStore is not required to implement a snapshot
|
||||
// driver but can be used to handle the persistence and transactional
|
||||
// complexities of a driver implementation.
|
||||
type MetaStore interface {
|
||||
// TransactionContext creates a new transaction context.
|
||||
TransactionContext(ctx context.Context, writable bool) (context.Context, Transactor, error)
|
||||
|
||||
// Stat returns the snapshot stat Info directly from
|
||||
// the metadata.
|
||||
Stat(ctx context.Context, key string) (snapshot.Info, error)
|
||||
|
||||
// Walk iterates through all metadata for the stored
|
||||
// snapshots and calls the provided function for each.
|
||||
Walk(ctx context.Context, fn func(context.Context, snapshot.Info) error) error
|
||||
|
||||
// CreateActive creates a new active snapshot transaction referenced by
|
||||
// the provided key. The new active snapshot will have the provided
|
||||
// parent. If the readonly option is given, the active snapshot will be
|
||||
// marked as readonly and can only be removed, and not committed. The
|
||||
// provided context must contain a writable transaction.
|
||||
CreateActive(ctx context.Context, key, parent string, readonly bool) (Active, error)
|
||||
|
||||
// GetActive returns the metadata for the active snapshot transaction
|
||||
// referenced by the given key.
|
||||
GetActive(ctx context.Context, key string) (Active, error)
|
||||
|
||||
// Remove removes a snapshot from the metastore. The provided context
|
||||
// must contain a writable transaction. The string identifier for the
|
||||
// snapshot is returned as well as the kind.
|
||||
Remove(ctx context.Context, key string) (string, snapshot.Kind, error)
|
||||
|
||||
// Commit renames the active snapshot transaction referenced by `key`
|
||||
// as a committed snapshot referenced by `Name`. The resulting snapshot
|
||||
// will be committed and readonly. The `key` reference will no longer
|
||||
// be available for lookup or removal. The returned string identifier
|
||||
// for the committed snapshot is the same identifier of the original
|
||||
// active snapshot.
|
||||
Commit(ctx context.Context, key, name string) (string, error)
|
||||
}
|
||||
|
||||
// Transactor is used to finalize an active transaction.
|
||||
type Transactor interface {
|
||||
// Commit commits any changes made during the transaction.
|
||||
// Commit commits any changes made during the transaction. On error a
|
||||
// caller is expected to clean up any resources which would have relied
|
||||
// on data mutated as part of this transaction. Only writable
|
||||
// transactions can commit, non-writable must call Rollback.
|
||||
Commit() error
|
||||
|
||||
// Rollback rolls back any changes made during the transaction.
|
||||
// Rollback rolls back any changes made during the transaction. This
|
||||
// must be called on all non-writable transactions and aborted writable
|
||||
// transaction.
|
||||
Rollback() error
|
||||
}
|
||||
|
||||
|
@ -67,3 +36,47 @@ type Active struct {
|
|||
ParentIDs []string
|
||||
Readonly bool
|
||||
}
|
||||
|
||||
// MetaStore is used to store metadata related to a snapshot driver. The
|
||||
// MetaStore is intended to store metadata related to name, state and
|
||||
// parentage. Using the MetaStore is not required to implement a snapshot
|
||||
// driver but can be used to handle the persistence and transactional
|
||||
// complexities of a driver implementation.
|
||||
type MetaStore struct {
|
||||
dbfile string
|
||||
}
|
||||
|
||||
// NewMetaStore returns a snapshot MetaStore for storage of metadata related to
|
||||
// a snapshot driver backed by a bolt file database. This implementation is
|
||||
// strongly consistent and does all metadata changes in a transaction to prevent
|
||||
// against process crashes causing inconsistent metadata state.
|
||||
func NewMetaStore(dbfile string) (*MetaStore, error) {
|
||||
return &MetaStore{
|
||||
dbfile: dbfile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type transactionKey struct{}
|
||||
|
||||
// TransactionContext creates a new transaction context. The writable value
|
||||
// should be set to true for transactions which are expected to mutate data.
|
||||
func (ms *MetaStore) TransactionContext(ctx context.Context, writable bool) (context.Context, Transactor, error) {
|
||||
db, err := bolt.Open(ms.dbfile, 0600, nil)
|
||||
if err != nil {
|
||||
return ctx, nil, errors.Wrap(err, "failed to open database file")
|
||||
}
|
||||
|
||||
tx, err := db.Begin(writable)
|
||||
if err != nil {
|
||||
return ctx, nil, errors.Wrap(err, "failed to start transaction")
|
||||
}
|
||||
|
||||
t := &boltFileTransactor{
|
||||
db: db,
|
||||
tx: tx,
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, transactionKey{}, t)
|
||||
|
||||
return ctx, t, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package testsuite
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,13 +6,11 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
)
|
||||
|
||||
// Benchmarks returns a benchmark suite using the provided metadata store
|
||||
// creation method
|
||||
func Benchmarks(b *testing.B, name string, metaFn func(context.Context, string) (storage.MetaStore, error)) {
|
||||
func Benchmarks(b *testing.B, name string, metaFn metaFactory) {
|
||||
b.Run("StatActive", makeBench(b, name, metaFn, statActiveBenchmark))
|
||||
b.Run("StatCommitted", makeBench(b, name, metaFn, statCommittedBenchmark))
|
||||
b.Run("CreateActive", makeBench(b, name, metaFn, createActiveBenchmark))
|
||||
|
@ -24,7 +22,7 @@ func Benchmarks(b *testing.B, name string, metaFn func(context.Context, string)
|
|||
}
|
||||
|
||||
// makeBench creates a benchmark with a writable transaction
|
||||
func makeBench(b *testing.B, name string, metaFn func(context.Context, string) (storage.MetaStore, error), fn func(context.Context, *testing.B, storage.MetaStore)) func(b *testing.B) {
|
||||
func makeBench(b *testing.B, name string, metaFn metaFactory, fn func(context.Context, *testing.B, *MetaStore)) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
tmpDir, err := ioutil.TempDir("", "metastore-bench-"+name+"-")
|
||||
|
@ -33,7 +31,7 @@ func makeBench(b *testing.B, name string, metaFn func(context.Context, string) (
|
|||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ms, err := metaFn(ctx, tmpDir)
|
||||
ms, err := metaFn(tmpDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
@ -49,7 +47,7 @@ func makeBench(b *testing.B, name string, metaFn func(context.Context, string) (
|
|||
}
|
||||
}
|
||||
|
||||
func openCloseWritable(b *testing.B, name string, metaFn func(context.Context, string) (storage.MetaStore, error)) func(b *testing.B) {
|
||||
func openCloseWritable(b *testing.B, name string, metaFn metaFactory) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
tmpDir, err := ioutil.TempDir("", "metastore-bench-"+name+"-")
|
||||
|
@ -58,7 +56,7 @@ func openCloseWritable(b *testing.B, name string, metaFn func(context.Context, s
|
|||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ms, err := metaFn(ctx, tmpDir)
|
||||
ms, err := metaFn(tmpDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
@ -77,7 +75,7 @@ func openCloseWritable(b *testing.B, name string, metaFn func(context.Context, s
|
|||
}
|
||||
}
|
||||
|
||||
func openCloseReadonly(b *testing.B, name string, metaFn func(context.Context, string) (storage.MetaStore, error)) func(b *testing.B) {
|
||||
func openCloseReadonly(b *testing.B, name string, metaFn metaFactory) func(b *testing.B) {
|
||||
return func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
tmpDir, err := ioutil.TempDir("", "metastore-bench-"+name+"-")
|
||||
|
@ -86,7 +84,7 @@ func openCloseReadonly(b *testing.B, name string, metaFn func(context.Context, s
|
|||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ms, err := metaFn(ctx, tmpDir)
|
||||
ms, err := metaFn(tmpDir)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
@ -105,112 +103,112 @@ func openCloseReadonly(b *testing.B, name string, metaFn func(context.Context, s
|
|||
}
|
||||
}
|
||||
|
||||
func createActiveFromBase(ctx context.Context, ms storage.MetaStore, active, base string) error {
|
||||
if _, err := ms.CreateActive(ctx, "bottom", "", false); err != nil {
|
||||
func createActiveFromBase(ctx context.Context, ms *MetaStore, active, base string) error {
|
||||
if _, err := CreateActive(ctx, "bottom", "", false); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ms.Commit(ctx, "bottom", base); err != nil {
|
||||
if _, err := CommitActive(ctx, "bottom", base); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := ms.CreateActive(ctx, active, base, false)
|
||||
_, err := CreateActive(ctx, active, base, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func statActiveBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func statActiveBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
if err := createActiveFromBase(ctx, ms, "active", "base"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ms.Stat(ctx, "active")
|
||||
_, err := GetInfo(ctx, "active")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func statCommittedBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func statCommittedBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
if err := createActiveFromBase(ctx, ms, "active", "base"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if _, err := ms.Commit(ctx, "active", "committed"); err != nil {
|
||||
if _, err := CommitActive(ctx, "active", "committed"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ms.Stat(ctx, "committed")
|
||||
_, err := GetInfo(ctx, "committed")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createActiveBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func createActiveBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := ms.CreateActive(ctx, "active", "", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active", "", false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StopTimer()
|
||||
if _, _, err := ms.Remove(ctx, "active"); err != nil {
|
||||
if _, _, err := Remove(ctx, "active"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func removeBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func removeBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
if _, err := ms.CreateActive(ctx, "active", "", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active", "", false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
if _, _, err := ms.Remove(ctx, "active"); err != nil {
|
||||
if _, _, err := Remove(ctx, "active"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func commitBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func commitBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
b.StopTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := ms.CreateActive(ctx, "active", "", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active", "", false); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
if _, err := ms.Commit(ctx, "active", "committed"); err != nil {
|
||||
if _, err := CommitActive(ctx, "active", "committed"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StopTimer()
|
||||
if _, _, err := ms.Remove(ctx, "committed"); err != nil {
|
||||
if _, _, err := Remove(ctx, "committed"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getActiveBenchmark(ctx context.Context, b *testing.B, ms storage.MetaStore) {
|
||||
func getActiveBenchmark(ctx context.Context, b *testing.B, ms *MetaStore) {
|
||||
var base string
|
||||
for i := 1; i <= 10; i++ {
|
||||
if _, err := ms.CreateActive(ctx, "tmp", base, false); err != nil {
|
||||
if _, err := CreateActive(ctx, "tmp", base, false); err != nil {
|
||||
b.Fatalf("create active failed: %+v", err)
|
||||
}
|
||||
base = fmt.Sprintf("base-%d", i)
|
||||
if _, err := ms.Commit(ctx, "tmp", base); err != nil {
|
||||
if _, err := CommitActive(ctx, "tmp", base); err != nil {
|
||||
b.Fatalf("commit failed: %+v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if _, err := ms.CreateActive(ctx, "active", base, false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active", base, false); err != nil {
|
||||
b.Fatalf("create active failed: %+v", err)
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := ms.GetActive(ctx, "active"); err != nil {
|
||||
if _, err := GetActive(ctx, "active"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package testsuite
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -7,22 +7,21 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/snapshot"
|
||||
"github.com/containerd/containerd/snapshot/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testFunc func(context.Context, *testing.T, storage.MetaStore)
|
||||
type testFunc func(context.Context, *testing.T, *MetaStore)
|
||||
|
||||
type metaFactory func(context.Context, string) (storage.MetaStore, error)
|
||||
type metaFactory func(string) (*MetaStore, error)
|
||||
|
||||
type populateFunc func(context.Context, storage.MetaStore) error
|
||||
type populateFunc func(context.Context, *MetaStore) error
|
||||
|
||||
// MetaStoreSuite runs a test suite on the metastore given a factory function.
|
||||
func MetaStoreSuite(t *testing.T, name string, meta func(ctx context.Context, root string) (storage.MetaStore, error)) {
|
||||
t.Run("Stat", makeTest(t, name, meta, inReadTransaction(testStat, basePopulate)))
|
||||
t.Run("StatNotExist", makeTest(t, name, meta, inReadTransaction(testStatNotExist, basePopulate)))
|
||||
t.Run("StatEmptyDB", makeTest(t, name, meta, inReadTransaction(testStatNotExist, nil)))
|
||||
func MetaStoreSuite(t *testing.T, name string, meta func(root string) (*MetaStore, error)) {
|
||||
t.Run("GetInfo", makeTest(t, name, meta, inReadTransaction(testGetInfo, basePopulate)))
|
||||
t.Run("GetInfoNotExist", makeTest(t, name, meta, inReadTransaction(testGetInfoNotExist, basePopulate)))
|
||||
t.Run("GetInfoEmptyDB", makeTest(t, name, meta, inReadTransaction(testGetInfoNotExist, nil)))
|
||||
t.Run("Walk", makeTest(t, name, meta, inReadTransaction(testWalk, basePopulate)))
|
||||
t.Run("GetActive", makeTest(t, name, meta, testGetActive))
|
||||
t.Run("GetActiveNotExist", makeTest(t, name, meta, inReadTransaction(testGetActiveNotExist, basePopulate)))
|
||||
|
@ -52,7 +51,7 @@ func makeTest(t *testing.T, name string, metaFn metaFactory, fn testFunc) func(t
|
|||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ms, err := metaFn(ctx, tmpDir)
|
||||
ms, err := metaFn(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -62,7 +61,7 @@ func makeTest(t *testing.T, name string, metaFn metaFactory, fn testFunc) func(t
|
|||
}
|
||||
|
||||
func inReadTransaction(fn testFunc, pf populateFunc) testFunc {
|
||||
return func(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
return func(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if pf != nil {
|
||||
ctx, tx, err := ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
|
@ -97,7 +96,7 @@ func inReadTransaction(fn testFunc, pf populateFunc) testFunc {
|
|||
}
|
||||
|
||||
func inWriteTransaction(fn testFunc) testFunc {
|
||||
return func(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
return func(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
ctx, tx, err := ms.TransactionContext(ctx, true)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to start transaction: %+v", err)
|
||||
|
@ -125,32 +124,32 @@ func inWriteTransaction(fn testFunc) testFunc {
|
|||
// - "active-3": active with parent "committed-2"
|
||||
// - "active-4": readonly active without parent"
|
||||
// - "active-5": readonly active with parent "committed-2"
|
||||
func basePopulate(ctx context.Context, ms storage.MetaStore) error {
|
||||
if _, err := ms.CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
|
||||
func basePopulate(ctx context.Context, ms *MetaStore) error {
|
||||
if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.Commit(ctx, "committed-tmp-1", "committed-1"); err != nil {
|
||||
if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "committed-tmp-2", "committed-1", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "committed-tmp-2", "committed-1", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.Commit(ctx, "committed-tmp-2", "committed-2"); err != nil {
|
||||
if _, err := CommitActive(ctx, "committed-tmp-2", "committed-2"); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "active-1", "", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active-1", "", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "active-2", "committed-1", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active-2", "committed-1", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "active-3", "committed-2", false); err != nil {
|
||||
if _, err := CreateActive(ctx, "active-3", "committed-2", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "active-4", "", true); err != nil {
|
||||
if _, err := CreateActive(ctx, "active-4", "", true); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.CreateActive(ctx, "active-5", "committed-2", true); err != nil {
|
||||
if _, err := CreateActive(ctx, "active-5", "committed-2", true); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
return nil
|
||||
|
@ -237,24 +236,24 @@ func assertExist(t *testing.T, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func testStat(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testGetInfo(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
for key, expected := range baseInfo {
|
||||
info, err := ms.Stat(ctx, key)
|
||||
info, err := GetInfo(ctx, key)
|
||||
if err != nil {
|
||||
t.Fatalf("Stat on %v failed: %+v", key, err)
|
||||
t.Fatalf("GetInfo on %v failed: %+v", key, err)
|
||||
}
|
||||
assert.Equal(t, expected, info)
|
||||
}
|
||||
}
|
||||
|
||||
func testStatNotExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, err := ms.Stat(ctx, "active-not-exist")
|
||||
func testGetInfoNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, err := GetInfo(ctx, "active-not-exist")
|
||||
assertNotExist(t, err)
|
||||
}
|
||||
|
||||
func testWalk(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testWalk(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
found := map[string]snapshot.Info{}
|
||||
err := ms.Walk(ctx, func(ctx context.Context, info snapshot.Info) error {
|
||||
err := WalkInfo(ctx, func(ctx context.Context, info snapshot.Info) error {
|
||||
if _, ok := found[info.Name]; ok {
|
||||
return errors.Errorf("entry already encountered")
|
||||
}
|
||||
|
@ -267,13 +266,13 @@ func testWalk(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
assert.Equal(t, baseInfo, found)
|
||||
}
|
||||
|
||||
func testGetActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
activeMap := map[string]storage.Active{}
|
||||
populate := func(ctx context.Context, ms storage.MetaStore) error {
|
||||
if _, err := ms.CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
|
||||
func testGetActive(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
activeMap := map[string]Active{}
|
||||
populate := func(ctx context.Context, ms *MetaStore) error {
|
||||
if _, err := CreateActive(ctx, "committed-tmp-1", "", false); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
if _, err := ms.Commit(ctx, "committed-tmp-1", "committed-1"); err != nil {
|
||||
if _, err := CommitActive(ctx, "committed-tmp-1", "committed-1"); err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
|
||||
|
@ -299,7 +298,7 @@ func testGetActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
Readonly: true,
|
||||
},
|
||||
} {
|
||||
active, err := ms.CreateActive(ctx, opts.Name, opts.Parent, opts.Readonly)
|
||||
active, err := CreateActive(ctx, opts.Name, opts.Parent, opts.Readonly)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create active")
|
||||
}
|
||||
|
@ -308,9 +307,9 @@ func testGetActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
return nil
|
||||
}
|
||||
|
||||
test := func(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
test := func(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
for key, expected := range activeMap {
|
||||
active, err := ms.GetActive(ctx, key)
|
||||
active, err := GetActive(ctx, key)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get active: %+v", err)
|
||||
}
|
||||
|
@ -321,18 +320,18 @@ func testGetActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
inReadTransaction(test, populate)(ctx, t, ms)
|
||||
}
|
||||
|
||||
func testGetActiveCommitted(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, err := ms.GetActive(ctx, "committed-1")
|
||||
func testGetActiveCommitted(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, err := GetActive(ctx, "committed-1")
|
||||
assertNotActive(t, err)
|
||||
}
|
||||
|
||||
func testGetActiveNotExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, err := ms.GetActive(ctx, "active-not-exist")
|
||||
func testGetActiveNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, err := GetActive(ctx, "active-not-exist")
|
||||
assertNotExist(t, err)
|
||||
}
|
||||
|
||||
func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
a1, err := ms.CreateActive(ctx, "active-1", "", false)
|
||||
func testCreateActive(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
a1, err := CreateActive(ctx, "active-1", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -340,7 +339,7 @@ func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected writable active")
|
||||
}
|
||||
|
||||
a2, err := ms.CreateActive(ctx, "active-2", "", true)
|
||||
a2, err := CreateActive(ctx, "active-2", "", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -351,7 +350,7 @@ func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected readonly active")
|
||||
}
|
||||
|
||||
commitID, err := ms.Commit(ctx, "active-1", "committed-1")
|
||||
commitID, err := CommitActive(ctx, "active-1", "committed-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -359,7 +358,7 @@ func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Snapshot identifier must not change on commit")
|
||||
}
|
||||
|
||||
a3, err := ms.CreateActive(ctx, "active-3", "committed-1", false)
|
||||
a3, err := CreateActive(ctx, "active-3", "committed-1", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -376,7 +375,7 @@ func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected writable active")
|
||||
}
|
||||
|
||||
a4, err := ms.CreateActive(ctx, "active-4", "committed-1", true)
|
||||
a4, err := CreateActive(ctx, "active-4", "committed-1", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -394,31 +393,31 @@ func testCreateActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
}
|
||||
}
|
||||
|
||||
func testCreateActiveExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testCreateActiveExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, err := ms.CreateActive(ctx, "active-1", "", false)
|
||||
_, err := CreateActive(ctx, "active-1", "", false)
|
||||
assertExist(t, err)
|
||||
_, err = ms.CreateActive(ctx, "committed-1", "", false)
|
||||
_, err = CreateActive(ctx, "committed-1", "", false)
|
||||
assertExist(t, err)
|
||||
}
|
||||
|
||||
func testCreateActiveNotExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, err := ms.CreateActive(ctx, "active-1", "does-not-exist", false)
|
||||
func testCreateActiveNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, err := CreateActive(ctx, "active-1", "does-not-exist", false)
|
||||
assertNotExist(t, err)
|
||||
}
|
||||
|
||||
func testCreateActiveFromActive(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testCreateActiveFromActive(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, err := ms.CreateActive(ctx, "active-new", "active-1", false)
|
||||
_, err := CreateActive(ctx, "active-new", "active-1", false)
|
||||
assertNotCommitted(t, err)
|
||||
}
|
||||
|
||||
func testCommit(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
a1, err := ms.CreateActive(ctx, "active-1", "", false)
|
||||
func testCommit(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
a1, err := CreateActive(ctx, "active-1", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -426,7 +425,7 @@ func testCommit(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected writable active")
|
||||
}
|
||||
|
||||
commitID, err := ms.Commit(ctx, "active-1", "committed-1")
|
||||
commitID, err := CommitActive(ctx, "active-1", "committed-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -434,50 +433,50 @@ func testCommit(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Snapshot identifier must not change on commit")
|
||||
}
|
||||
|
||||
_, err = ms.GetActive(ctx, "active-1")
|
||||
_, err = GetActive(ctx, "active-1")
|
||||
assertNotExist(t, err)
|
||||
_, err = ms.GetActive(ctx, "committed-1")
|
||||
_, err = GetActive(ctx, "committed-1")
|
||||
assertNotActive(t, err)
|
||||
}
|
||||
|
||||
func testCommitNotExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, err := ms.Commit(ctx, "active-not-exist", "committed-1")
|
||||
func testCommitNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, err := CommitActive(ctx, "active-not-exist", "committed-1")
|
||||
assertNotExist(t, err)
|
||||
}
|
||||
|
||||
func testCommitExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testCommitExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, err := ms.Commit(ctx, "active-1", "committed-1")
|
||||
_, err := CommitActive(ctx, "active-1", "committed-1")
|
||||
assertExist(t, err)
|
||||
}
|
||||
|
||||
func testCommitCommitted(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testCommitCommitted(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, err := ms.Commit(ctx, "committed-1", "committed-3")
|
||||
_, err := CommitActive(ctx, "committed-1", "committed-3")
|
||||
assertNotActive(t, err)
|
||||
}
|
||||
|
||||
func testCommitReadonly(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testCommitReadonly(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, err := ms.Commit(ctx, "active-5", "committed-3")
|
||||
_, err := CommitActive(ctx, "active-5", "committed-3")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error committing readonly active")
|
||||
}
|
||||
}
|
||||
|
||||
func testRemove(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
a1, err := ms.CreateActive(ctx, "active-1", "", false)
|
||||
func testRemove(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
a1, err := CreateActive(ctx, "active-1", "", false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
commitID, err := ms.Commit(ctx, "active-1", "committed-1")
|
||||
commitID, err := CommitActive(ctx, "active-1", "committed-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -485,20 +484,20 @@ func testRemove(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Snapshot identifier must not change on commit")
|
||||
}
|
||||
|
||||
a2, err := ms.CreateActive(ctx, "active-2", "committed-1", true)
|
||||
a2, err := CreateActive(ctx, "active-2", "committed-1", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
a3, err := ms.CreateActive(ctx, "active-3", "committed-1", true)
|
||||
a3, err := CreateActive(ctx, "active-3", "committed-1", true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, _, err = ms.Remove(ctx, "active-1")
|
||||
_, _, err = Remove(ctx, "active-1")
|
||||
assertNotExist(t, err)
|
||||
|
||||
r3, k3, err := ms.Remove(ctx, "active-3")
|
||||
r3, k3, err := Remove(ctx, "active-3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -509,7 +508,7 @@ func testRemove(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected active kind, got %v", k3)
|
||||
}
|
||||
|
||||
r2, k2, err := ms.Remove(ctx, "active-2")
|
||||
r2, k2, err := Remove(ctx, "active-2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -520,7 +519,7 @@ func testRemove(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
t.Fatal("Expected active kind, got %v", k2)
|
||||
}
|
||||
|
||||
r1, k1, err := ms.Remove(ctx, "committed-1")
|
||||
r1, k1, err := Remove(ctx, "committed-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -532,21 +531,21 @@ func testRemove(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
|||
}
|
||||
}
|
||||
|
||||
func testRemoveWithChildren(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
func testRemoveWithChildren(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
if err := basePopulate(ctx, ms); err != nil {
|
||||
t.Fatalf("Populate failed: %+v", err)
|
||||
}
|
||||
_, _, err := ms.Remove(ctx, "committed-1")
|
||||
_, _, err := Remove(ctx, "committed-1")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected removal of snapshot with children to error")
|
||||
}
|
||||
_, _, err = ms.Remove(ctx, "committed-1")
|
||||
_, _, err = Remove(ctx, "committed-1")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected removal of snapshot with children to error")
|
||||
}
|
||||
}
|
||||
|
||||
func testRemoveNotExist(ctx context.Context, t *testing.T, ms storage.MetaStore) {
|
||||
_, _, err := ms.Remove(ctx, "does-not-exist")
|
||||
func testRemoveNotExist(ctx context.Context, t *testing.T, ms *MetaStore) {
|
||||
_, _, err := Remove(ctx, "does-not-exist")
|
||||
assertNotExist(t, err)
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
// Code generated by protoc-gen-gogo.
|
||||
// source: github.com/containerd/containerd/snapshot/storage/boltdb/record.proto
|
||||
// source: github.com/containerd/containerd/snapshot/storage/proto/record.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package boltdb is a generated protocol buffer package.
|
||||
Package proto is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
github.com/containerd/containerd/snapshot/storage/boltdb/record.proto
|
||||
github.com/containerd/containerd/snapshot/storage/proto/record.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Snapshot
|
||||
*/
|
||||
package boltdb
|
||||
package proto
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import proto1 "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
|
@ -24,7 +24,7 @@ import reflect "reflect"
|
|||
import io "io"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = proto1.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
|
@ -32,7 +32,7 @@ var _ = math.Inf
|
|||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto1.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Kind defines the kind of snapshot.
|
||||
type Kind int32
|
||||
|
@ -54,10 +54,12 @@ var Kind_value = map[string]int32{
|
|||
}
|
||||
|
||||
func (x Kind) String() string {
|
||||
return proto.EnumName(Kind_name, int32(x))
|
||||
return proto1.EnumName(Kind_name, int32(x))
|
||||
}
|
||||
func (Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptorRecord, []int{0} }
|
||||
|
||||
// Snapshot defines the storage type for a snapshot in the
|
||||
// metadata store.
|
||||
type Snapshot struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"`
|
||||
|
@ -70,8 +72,8 @@ func (*Snapshot) ProtoMessage() {}
|
|||
func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptorRecord, []int{0} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Snapshot)(nil), "containerd.v1.Snapshot")
|
||||
proto.RegisterEnum("containerd.v1.Kind", Kind_name, Kind_value)
|
||||
proto1.RegisterType((*Snapshot)(nil), "containerd.v1.Snapshot")
|
||||
proto1.RegisterEnum("containerd.v1.Kind", Kind_name, Kind_value)
|
||||
}
|
||||
func (m *Snapshot) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
|
@ -440,29 +442,28 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/containerd/containerd/snapshot/storage/boltdb/record.proto", fileDescriptorRecord)
|
||||
proto1.RegisterFile("github.com/containerd/containerd/snapshot/storage/proto/record.proto", fileDescriptorRecord)
|
||||
}
|
||||
|
||||
var fileDescriptorRecord = []byte{
|
||||
// 309 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4d, 0xcf, 0x2c, 0xc9,
|
||||
// 304 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x49, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d,
|
||||
0x4a, 0x41, 0x66, 0x16, 0xe7, 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x17, 0x97, 0xe4, 0x17,
|
||||
0x25, 0xa6, 0xa7, 0xea, 0x27, 0xe5, 0xe7, 0x94, 0xa4, 0x24, 0xe9, 0x17, 0xa5, 0x26, 0xe7, 0x17,
|
||||
0xa5, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xf1, 0x22, 0x34, 0xe8, 0x95, 0x19, 0x4a, 0x89,
|
||||
0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x65, 0xf4, 0x41, 0x2c, 0x88, 0x22, 0xa5, 0x7a, 0x2e, 0x8e, 0x60,
|
||||
0xa8, 0x61, 0x42, 0x62, 0x5c, 0x4c, 0x99, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x2c, 0x4e, 0x6c,
|
||||
0x8f, 0xee, 0xc9, 0x33, 0x79, 0xba, 0x04, 0x31, 0x65, 0xa6, 0x08, 0x89, 0x71, 0xb1, 0x15, 0x24,
|
||||
0x16, 0xa5, 0xe6, 0x95, 0x48, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0x41, 0x79, 0x42, 0xea, 0x5c,
|
||||
0x2c, 0xd9, 0x99, 0x79, 0x29, 0x12, 0x2c, 0x0a, 0x8c, 0x1a, 0x7c, 0x46, 0xc2, 0x7a, 0x28, 0xf6,
|
||||
0xe9, 0x79, 0x67, 0xe6, 0xa5, 0x04, 0x81, 0x15, 0x08, 0x49, 0x71, 0x71, 0x14, 0xa5, 0x26, 0xa6,
|
||||
0xe4, 0xe7, 0xe5, 0x54, 0x4a, 0xb0, 0x2a, 0x30, 0x6a, 0x70, 0x04, 0xc1, 0xf9, 0x5a, 0x41, 0x5c,
|
||||
0x2c, 0xde, 0x10, 0x35, 0x6c, 0x8e, 0xce, 0x21, 0x9e, 0x61, 0xae, 0x02, 0x0c, 0x52, 0x7c, 0x5d,
|
||||
0x73, 0x15, 0xb8, 0x40, 0xa2, 0x8e, 0xc9, 0x25, 0x99, 0x65, 0xa9, 0x42, 0x0a, 0x5c, 0x9c, 0xce,
|
||||
0xfe, 0xbe, 0xbe, 0x9e, 0x21, 0x21, 0xae, 0x2e, 0x02, 0x8c, 0x52, 0x82, 0x5d, 0x73, 0x15, 0x78,
|
||||
0x41, 0xd2, 0xce, 0xf9, 0xb9, 0xb9, 0x99, 0x25, 0x25, 0xa9, 0x29, 0x52, 0x3c, 0x1d, 0x8b, 0xe5,
|
||||
0x18, 0x76, 0x2d, 0x91, 0x03, 0x9b, 0xe5, 0x24, 0x71, 0xe2, 0xa1, 0x1c, 0xc3, 0x8d, 0x87, 0x72,
|
||||
0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23,
|
||||
0x39, 0xc6, 0x24, 0x36, 0xb0, 0xaf, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x73, 0x89, 0x2b,
|
||||
0x02, 0x63, 0x01, 0x00, 0x00,
|
||||
0x25, 0xa6, 0xa7, 0xea, 0x17, 0x14, 0xe5, 0x97, 0xe4, 0xeb, 0x17, 0xa5, 0x26, 0xe7, 0x17, 0xa5,
|
||||
0xe8, 0x81, 0x39, 0x42, 0xbc, 0x08, 0xf5, 0x7a, 0x65, 0x86, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9,
|
||||
0x10, 0x65, 0x20, 0x16, 0x44, 0x91, 0x52, 0x3d, 0x17, 0x47, 0x30, 0xd4, 0x2c, 0x21, 0x31, 0x2e,
|
||||
0xa6, 0xcc, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x16, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c,
|
||||
0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0xc4, 0xb8, 0xd8, 0x0a, 0x12, 0x8b, 0x52, 0xf3, 0x4a, 0x24,
|
||||
0x98, 0x14, 0x18, 0x35, 0x38, 0x83, 0xa0, 0x3c, 0x21, 0x75, 0x2e, 0x96, 0xec, 0xcc, 0xbc, 0x14,
|
||||
0x09, 0x16, 0x05, 0x46, 0x0d, 0x3e, 0x23, 0x61, 0x3d, 0x14, 0xfb, 0xf4, 0xbc, 0x33, 0xf3, 0x52,
|
||||
0x82, 0xc0, 0x0a, 0x84, 0xa4, 0xb8, 0x38, 0x8a, 0x52, 0x13, 0x53, 0xf2, 0xf3, 0x72, 0x2a, 0x25,
|
||||
0x58, 0x15, 0x18, 0x35, 0x38, 0x82, 0xe0, 0x7c, 0xad, 0x20, 0x2e, 0x16, 0x6f, 0x88, 0x1a, 0x36,
|
||||
0x47, 0xe7, 0x10, 0xcf, 0x30, 0x57, 0x01, 0x06, 0x29, 0xbe, 0xae, 0xb9, 0x0a, 0x5c, 0x20, 0x51,
|
||||
0xc7, 0xe4, 0x92, 0xcc, 0xb2, 0x54, 0x21, 0x05, 0x2e, 0x4e, 0x67, 0x7f, 0x5f, 0x5f, 0xcf, 0x90,
|
||||
0x10, 0x57, 0x17, 0x01, 0x46, 0x29, 0xc1, 0xae, 0xb9, 0x0a, 0xbc, 0x20, 0x69, 0xe7, 0xfc, 0xdc,
|
||||
0xdc, 0xcc, 0x92, 0x92, 0xd4, 0x14, 0x29, 0x9e, 0x8e, 0xc5, 0x72, 0x0c, 0xbb, 0x96, 0xc8, 0x81,
|
||||
0xcd, 0x72, 0x92, 0x38, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c,
|
||||
0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x12, 0x1b, 0xd8,
|
||||
0xd7, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x89, 0x2c, 0x37, 0x62, 0x01, 0x00, 0x00,
|
||||
}
|
|
@ -16,6 +16,8 @@ enum Kind {
|
|||
COMMITTED = 1 [(gogoproto.enumvalue_customname) = "KindCommitted"];
|
||||
}
|
||||
|
||||
// Snapshot defines the storage type for a snapshot in the
|
||||
// metadata store.
|
||||
message Snapshot {
|
||||
uint64 id = 1 [(gogoproto.customname) = "ID"];
|
||||
string parent = 2;
|
Loading…
Reference in a new issue