From b319ba7c5abd1a76eead6ee91a6f6899286c8b81 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Fri, 24 Mar 2017 17:16:41 -0700 Subject: [PATCH 1/3] Move boltdb implementation to storage package Removes storage interface and replaces with storage functions. Signed-off-by: Derek McGowan --- snapshot/btrfs/btrfs.go | 29 +- snapshot/btrfs/btrfs_test.go | 7 +- snapshot/overlay/overlay.go | 35 ++- snapshot/overlay/overlay_test.go | 26 +- snapshot/storage/{boltdb => }/bolt.go | 256 ++++++++---------- snapshot/storage/bolt_test.go | 21 ++ snapshot/storage/boltdb/bolt_test.go | 25 -- snapshot/storage/metastore.go | 88 +++--- .../bench.go => metastore_bench_test.go} | 66 +++-- .../testsuite.go => metastore_test.go} | 161 ++++++----- .../storage/{boltdb => proto}/record.pb.go | 61 +++-- .../storage/{boltdb => proto}/record.proto | 2 + 12 files changed, 373 insertions(+), 404 deletions(-) rename snapshot/storage/{boltdb => }/bolt.go (64%) create mode 100644 snapshot/storage/bolt_test.go delete mode 100644 snapshot/storage/boltdb/bolt_test.go rename snapshot/storage/{testsuite/bench.go => metastore_bench_test.go} (59%) rename snapshot/storage/{testsuite/testsuite.go => metastore_test.go} (68%) rename snapshot/storage/{boltdb => proto}/record.pb.go (80%) rename snapshot/storage/{boltdb => proto}/record.proto (88%) diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go index d74d2fd..99595f1 100644 --- a/snapshot/btrfs/btrfs.go +++ b/snapshot/btrfs/btrfs.go @@ -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,13 +31,7 @@ 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) }, }) } @@ -46,10 +39,10 @@ func init() { 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) { +func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) { var ( active = filepath.Join(root, "active") snapshots = filepath.Join(root, "snapshots") @@ -63,6 +56,10 @@ 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{ device: device, @@ -82,7 +79,7 @@ func (b *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro return snapshot.Info{}, err } defer t.Rollback() - return b.ms.Stat(ctx, key) + return storage.GetInfo(ctx, key) } // Walk the committed snapshots. @@ -92,7 +89,7 @@ func (b *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho 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) { @@ -116,7 +113,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 } @@ -187,7 +184,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") } @@ -225,7 +222,7 @@ func (b *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Moun 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") @@ -260,7 +257,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") } diff --git a/snapshot/btrfs/btrfs_test.go b/snapshot/btrfs/btrfs_test.go index 9b4f5ee..fc2194e 100644 --- a/snapshot/btrfs/btrfs_test.go +++ b/snapshot/btrfs/btrfs_test.go @@ -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) } diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index d202672..274f309 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -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 { root string - ms storage.MetaStore + ms *storage.MetaStore } type activeSnapshot struct { @@ -43,11 +37,16 @@ type activeSnapshot struct { readonly bool } -func NewSnapshotter(root string, ms storage.MetaStore) (snapshot.Snapshotter, error) { +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 + } + + if err := os.Mkdir(filepath.Join(root, "snapshots"), 0700); err != nil { return nil, err } @@ -68,7 +67,7 @@ func (o *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro 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) { @@ -88,7 +87,7 @@ func (o *Snapshotter) Mounts(ctx context.Context, key string) ([]containerd.Moun 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") @@ -101,7 +100,7 @@ func (o *Snapshotter) Commit(ctx context.Context, name, key string) error { 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") } @@ -125,7 +124,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") } @@ -160,7 +159,7 @@ func (o *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho 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) { @@ -202,7 +201,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 +210,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,7 +218,7 @@ 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") } diff --git a/snapshot/overlay/overlay_test.go b/snapshot/overlay/overlay_test.go index 86ef168..48d112f 100644 --- a/snapshot/overlay/overlay_test.go +++ b/snapshot/overlay/overlay_test.go @@ -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 @@ -160,7 +156,7 @@ func getBasePath(ctx context.Context, sn snapshot.Snapshotter, root, key string) } defer t.Rollback() - active, err := o.ms.GetActive(ctx, key) + active, err := storage.GetActive(ctx, key) if err != nil { panic(err) } @@ -175,7 +171,7 @@ func getParents(ctx context.Context, sn snapshot.Snapshotter, root, key string) 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) } diff --git a/snapshot/storage/boltdb/bolt.go b/snapshot/storage/bolt.go similarity index 64% rename from snapshot/storage/boltdb/bolt.go rename to snapshot/storage/bolt.go index 41914e7..a3e7a10 100644 --- a/snapshot/storage/boltdb/bolt.go +++ b/snapshot/storage/bolt.go @@ -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" ) @@ -23,91 +23,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 +57,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 +76,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 +102,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 +132,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 +149,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 +161,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 +188,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 +201,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 +229,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 +253,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 +298,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 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 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 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 +367,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") diff --git a/snapshot/storage/bolt_test.go b/snapshot/storage/bolt_test.go new file mode 100644 index 0000000..32454c3 --- /dev/null +++ b/snapshot/storage/bolt_test.go @@ -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")) + }) +} diff --git a/snapshot/storage/boltdb/bolt_test.go b/snapshot/storage/boltdb/bolt_test.go deleted file mode 100644 index 81c2dd5..0000000 --- a/snapshot/storage/boltdb/bolt_test.go +++ /dev/null @@ -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")) - }) -} diff --git a/snapshot/storage/metastore.go b/snapshot/storage/metastore.go index 31709df..f1d3218 100644 --- a/snapshot/storage/metastore.go +++ b/snapshot/storage/metastore.go @@ -3,51 +3,10 @@ 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. @@ -67,3 +26,46 @@ 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. +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 +} diff --git a/snapshot/storage/testsuite/bench.go b/snapshot/storage/metastore_bench_test.go similarity index 59% rename from snapshot/storage/testsuite/bench.go rename to snapshot/storage/metastore_bench_test.go index 8b36ef6..fd5cf2e 100644 --- a/snapshot/storage/testsuite/bench.go +++ b/snapshot/storage/metastore_bench_test.go @@ -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) } } diff --git a/snapshot/storage/testsuite/testsuite.go b/snapshot/storage/metastore_test.go similarity index 68% rename from snapshot/storage/testsuite/testsuite.go rename to snapshot/storage/metastore_test.go index fd38d28..f2e223f 100644 --- a/snapshot/storage/testsuite/testsuite.go +++ b/snapshot/storage/metastore_test.go @@ -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) } diff --git a/snapshot/storage/boltdb/record.pb.go b/snapshot/storage/proto/record.pb.go similarity index 80% rename from snapshot/storage/boltdb/record.pb.go rename to snapshot/storage/proto/record.pb.go index 3d2cfd2..6b09269 100644 --- a/snapshot/storage/boltdb/record.pb.go +++ b/snapshot/storage/proto/record.pb.go @@ -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, } diff --git a/snapshot/storage/boltdb/record.proto b/snapshot/storage/proto/record.proto similarity index 88% rename from snapshot/storage/boltdb/record.proto rename to snapshot/storage/proto/record.proto index 4d7a214..50fcfe6 100644 --- a/snapshot/storage/boltdb/record.proto +++ b/snapshot/storage/proto/record.proto @@ -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; From 9ffbfccdaf3b03a4b4a5c37fc4697c28796ff687 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Fri, 24 Mar 2017 17:21:03 -0700 Subject: [PATCH 2/3] Refactor overlay and btrfs to pass lint Signed-off-by: Derek McGowan --- snapshot/btrfs/btrfs.go | 25 ++++++++++++++----------- snapshot/overlay/overlay.go | 29 ++++++++++++++++------------- snapshot/overlay/overlay_test.go | 4 ++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go index 99595f1..f3c9851 100644 --- a/snapshot/btrfs/btrfs.go +++ b/snapshot/btrfs/btrfs.go @@ -36,12 +36,15 @@ func init() { }) } -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 } +// 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") @@ -61,7 +64,7 @@ func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) { return nil, err } - return &Snapshotter{ + return &snapshotter{ device: device, root: root, ms: ms, @@ -73,7 +76,7 @@ func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) { // // 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 @@ -83,7 +86,7 @@ func (b *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro } // 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 @@ -92,15 +95,15 @@ func (b *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho 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 @@ -145,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 @@ -171,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 @@ -217,7 +220,7 @@ 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 @@ -233,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 diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index 274f309..f883fa6 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -25,7 +25,7 @@ func init() { }) } -type Snapshotter struct { +type snapshotter struct { root string ms *storage.MetaStore } @@ -37,6 +37,9 @@ type activeSnapshot struct { readonly bool } +// 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 @@ -50,7 +53,7 @@ func NewSnapshotter(root string) (snapshot.Snapshotter, error) { return nil, err } - return &Snapshotter{ + return &snapshotter{ root: root, ms: ms, }, nil @@ -61,7 +64,7 @@ func NewSnapshotter(root string) (snapshot.Snapshotter, error) { // // 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 @@ -70,11 +73,11 @@ func (o *Snapshotter) Stat(ctx context.Context, key string) (snapshot.Info, erro 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) } @@ -82,7 +85,7 @@ 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 @@ -95,7 +98,7 @@ 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 @@ -111,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 @@ -153,7 +156,7 @@ 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 @@ -162,7 +165,7 @@ func (o *Snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho 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") @@ -225,7 +228,7 @@ func (o *Snapshotter) createActive(ctx context.Context, key, parent string, read 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 @@ -281,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") } diff --git a/snapshot/overlay/overlay_test.go b/snapshot/overlay/overlay_test.go index 48d112f..cf5f1be 100644 --- a/snapshot/overlay/overlay_test.go +++ b/snapshot/overlay/overlay_test.go @@ -149,7 +149,7 @@ 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) @@ -165,7 +165,7 @@ 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) From 1c2f5fe203823fcb8285180b40166b287ee4daf6 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 4 Apr 2017 13:31:38 -0700 Subject: [PATCH 3/3] Update documentation to storage package Signed-off-by: Derek McGowan --- snapshot/storage/bolt.go | 8 ++++++-- snapshot/storage/metastore.go | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/snapshot/storage/bolt.go b/snapshot/storage/bolt.go index a3e7a10..455f760 100644 --- a/snapshot/storage/bolt.go +++ b/snapshot/storage/bolt.go @@ -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 { @@ -301,7 +305,7 @@ func CommitActive(ctx context.Context, key, name string) (id string, err 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 errors.Errorf("no transaction in context") + return ErrNoTransaction } bkt := t.tx.Bucket(bucketKeyStorageVersion) if bkt == nil { @@ -313,7 +317,7 @@ func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bol func 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") + return ErrNoTransaction } bkt, err := t.tx.CreateBucketIfNotExists(bucketKeyStorageVersion) diff --git a/snapshot/storage/metastore.go b/snapshot/storage/metastore.go index f1d3218..c223d40 100644 --- a/snapshot/storage/metastore.go +++ b/snapshot/storage/metastore.go @@ -1,3 +1,8 @@ +// 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 ( @@ -9,10 +14,15 @@ import ( // 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 } @@ -48,7 +58,8 @@ func NewMetaStore(dbfile string) (*MetaStore, error) { type transactionKey struct{} -// TransactionContext creates a new transaction context. +// 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 {