From 8d892a651b0ec3884776623e4a1e65bbf22fcd6e Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 3 Oct 2017 15:48:05 -0700 Subject: [PATCH] Update metadata plugin initialization Updates metadata plugin to require content and snapshotter plugins be loaded and initializes with those plugins, keeping the metadata database structure static after initialization. Service plugins now only require metadata plugin access snapshotter or content stores through metadata, which was already required behavior of the services. Signed-off-by: Derek McGowan --- differ/differ.go | 7 +------ metadata/bolt.go | 9 +++++++-- metadata/content.go | 21 +++++++-------------- metadata/content_test.go | 2 +- metadata/db.go | 30 ++++++++++++++++++++++-------- metadata/snapshot.go | 31 +++++++++---------------------- metadata/snapshot_test.go | 6 +++--- server/server.go | 31 ++++++++++++++++++++++++++----- services/content/service.go | 9 +-------- services/snapshot/service.go | 26 ++++++-------------------- services/tasks/service.go | 7 +------ 11 files changed, 84 insertions(+), 95 deletions(-) diff --git a/differ/differ.go b/differ/differ.go index 1962989d1..8d7cd19d8 100644 --- a/differ/differ.go +++ b/differ/differ.go @@ -25,19 +25,14 @@ func init() { Type: plugin.DiffPlugin, ID: "walking", Requires: []plugin.Type{ - plugin.ContentPlugin, plugin.MetadataPlugin, }, Init: func(ic *plugin.InitContext) (interface{}, error) { - c, err := ic.Get(plugin.ContentPlugin) - if err != nil { - return nil, err - } md, err := ic.Get(plugin.MetadataPlugin) if err != nil { return nil, err } - return NewWalkingDiff(metadata.NewContentStore(md.(*metadata.DB), c.(content.Store))) + return NewWalkingDiff(md.(*metadata.DB).ContentStore()) }, }) } diff --git a/metadata/bolt.go b/metadata/bolt.go index ca9ceb939..2e4c35270 100644 --- a/metadata/bolt.go +++ b/metadata/bolt.go @@ -17,9 +17,14 @@ func WithTransactionContext(ctx context.Context, tx *bolt.Tx) context.Context { return context.WithValue(ctx, transactionKey{}, tx) } +type transactor interface { + View(fn func(*bolt.Tx) error) error + Update(fn func(*bolt.Tx) error) error +} + // view gets a bolt db transaction either from the context // or starts a new one with the provided bolt database. -func view(ctx context.Context, db *DB, fn func(*bolt.Tx) error) error { +func view(ctx context.Context, db transactor, fn func(*bolt.Tx) error) error { tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) if !ok { return db.View(fn) @@ -29,7 +34,7 @@ func view(ctx context.Context, db *DB, fn func(*bolt.Tx) error) error { // update gets a writable bolt db transaction either from the context // or starts a new one with the provided bolt database. -func update(ctx context.Context, db *DB, fn func(*bolt.Tx) error) error { +func update(ctx context.Context, db transactor, fn func(*bolt.Tx) error) error { tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx) if !ok { return db.Update(fn) diff --git a/metadata/content.go b/metadata/content.go index 7e4572666..50c98b984 100644 --- a/metadata/content.go +++ b/metadata/content.go @@ -19,23 +19,16 @@ import ( type contentStore struct { content.Store - db *DB + db transactor } -// NewContentStore returns a namespaced content store using an existing +// newContentStore returns a namespaced content store using an existing // content store interface. -func NewContentStore(db *DB, cs content.Store) content.Store { - db.storeL.Lock() - defer db.storeL.Unlock() - - if db.cs == nil { - db.cs = &contentStore{ - Store: cs, - db: db, - } +func newContentStore(db transactor, cs content.Store) content.Store { + return &contentStore{ + Store: cs, + db: db, } - return db.cs - } func (cs *contentStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) { @@ -360,7 +353,7 @@ type namespacedWriter struct { content.Writer ref string namespace string - db *DB + db transactor } func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { diff --git a/metadata/content_test.go b/metadata/content_test.go index fbddff2fc..2e4d833ab 100644 --- a/metadata/content_test.go +++ b/metadata/content_test.go @@ -23,7 +23,7 @@ func createContentStore(ctx context.Context, root string) (content.Store, func() return nil, nil, err } - return NewContentStore(NewDB(db), cs), func() error { + return NewDB(db, cs, nil).ContentStore(), func() error { return db.Close() }, nil } diff --git a/metadata/db.go b/metadata/db.go index 958983f4e..17027f12b 100644 --- a/metadata/db.go +++ b/metadata/db.go @@ -1,26 +1,40 @@ package metadata import ( - "sync" - "github.com/boltdb/bolt" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/snapshot" ) type DB struct { db *bolt.DB - - storeL sync.Mutex - ss map[string]*snapshotter - cs *contentStore + ss map[string]snapshot.Snapshotter + cs content.Store } -func NewDB(db *bolt.DB) *DB { +func NewDB(db *bolt.DB, cs content.Store, ss map[string]snapshot.Snapshotter) *DB { return &DB{ db: db, - ss: map[string]*snapshotter{}, + ss: ss, + cs: cs, } } +func (m *DB) ContentStore() content.Store { + if m.cs == nil { + return nil + } + return newContentStore(m, m.cs) +} + +func (m *DB) Snapshotter(name string) snapshot.Snapshotter { + sn, ok := m.ss[name] + if !ok { + return nil + } + return newSnapshotter(m, name, sn) +} + func (m *DB) View(fn func(*bolt.Tx) error) error { return m.db.View(fn) } diff --git a/metadata/snapshot.go b/metadata/snapshot.go index aeae2519a..4103827a4 100644 --- a/metadata/snapshot.go +++ b/metadata/snapshot.go @@ -19,26 +19,17 @@ import ( type snapshotter struct { snapshot.Snapshotter name string - db *DB + db transactor } -// NewSnapshotter returns a new Snapshotter which namespaces the given snapshot -// using the provided name and metadata store. -func NewSnapshotter(db *DB, name string, sn snapshot.Snapshotter) snapshot.Snapshotter { - db.storeL.Lock() - defer db.storeL.Unlock() - - ss, ok := db.ss[name] - if !ok { - ss = &snapshotter{ - Snapshotter: sn, - name: name, - db: db, - } - db.ss[name] = ss +// newSnapshotter returns a new Snapshotter which namespaces the given snapshot +// using the provided name and database. +func newSnapshotter(db transactor, name string, sn snapshot.Snapshotter) snapshot.Snapshotter { + return &snapshotter{ + Snapshotter: sn, + name: name, + db: db, } - - return ss } func createKey(id uint64, namespace, key string) string { @@ -466,11 +457,7 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error { } } - if err := bkt.DeleteBucket([]byte(key)); err != nil { - return err - } - - return nil + return bkt.DeleteBucket([]byte(key)) }) } diff --git a/metadata/snapshot_test.go b/metadata/snapshot_test.go index e7ad54e3e..5111c36b8 100644 --- a/metadata/snapshot_test.go +++ b/metadata/snapshot_test.go @@ -13,7 +13,7 @@ import ( "github.com/containerd/containerd/testutil" ) -func newSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, func() error, error) { +func newTestSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, func() error, error) { naiveRoot := filepath.Join(root, "naive") if err := os.Mkdir(naiveRoot, 0770); err != nil { return nil, nil, err @@ -28,7 +28,7 @@ func newSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, fun return nil, nil, err } - sn := NewSnapshotter(NewDB(db), "naive", snapshotter) + sn := NewDB(db, nil, map[string]snapshot.Snapshotter{"naive": snapshotter}).Snapshotter("naive") return sn, func() error { return db.Close() @@ -38,5 +38,5 @@ func newSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, fun func TestMetadata(t *testing.T) { // Snapshot tests require mounting, still requires root testutil.RequiresRoot(t) - testsuite.SnapshotterSuite(t, "Metadata", newSnapshotter) + testsuite.SnapshotterSuite(t, "Metadata", newTestSnapshotter) } diff --git a/server/server.go b/server/server.go index 1719898c4..8329adc67 100644 --- a/server/server.go +++ b/server/server.go @@ -12,19 +12,21 @@ import ( "github.com/boltdb/bolt" containers "github.com/containerd/containerd/api/services/containers/v1" - content "github.com/containerd/containerd/api/services/content/v1" + contentapi "github.com/containerd/containerd/api/services/content/v1" diff "github.com/containerd/containerd/api/services/diff/v1" eventsapi "github.com/containerd/containerd/api/services/events/v1" images "github.com/containerd/containerd/api/services/images/v1" namespaces "github.com/containerd/containerd/api/services/namespaces/v1" - snapshot "github.com/containerd/containerd/api/services/snapshot/v1" + snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1" tasks "github.com/containerd/containerd/api/services/tasks/v1" version "github.com/containerd/containerd/api/services/version/v1" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/content/local" "github.com/containerd/containerd/events" "github.com/containerd/containerd/log" "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/plugin" + "github.com/containerd/containerd/snapshot" metrics "github.com/docker/go-metrics" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "golang.org/x/net/context" @@ -176,15 +178,34 @@ func loadPlugins(config *Config) ([]*plugin.Registration, error) { plugin.Register(&plugin.Registration{ Type: plugin.MetadataPlugin, ID: "bolt", + Requires: []plugin.Type{ + plugin.ContentPlugin, + plugin.SnapshotPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { if err := os.MkdirAll(ic.Root, 0711); err != nil { return nil, err } + cs, err := ic.Get(plugin.ContentPlugin) + if err != nil { + return nil, err + } + + rawSnapshotters, err := ic.GetAll(plugin.SnapshotPlugin) + if err != nil { + return nil, err + } + + snapshotters := make(map[string]snapshot.Snapshotter) + for name, sn := range rawSnapshotters { + snapshotters[name] = sn.(snapshot.Snapshotter) + } + db, err := bolt.Open(filepath.Join(ic.Root, "meta.db"), 0644, nil) if err != nil { return nil, err } - return metadata.NewDB(db), nil + return metadata.NewDB(db, cs.(content.Store), snapshotters), nil }, }) @@ -204,7 +225,7 @@ func interceptor( ctx = log.WithModule(ctx, "tasks") case containers.ContainersServer: ctx = log.WithModule(ctx, "containers") - case content.ContentServer: + case contentapi.ContentServer: ctx = log.WithModule(ctx, "content") case images.ImagesServer: ctx = log.WithModule(ctx, "images") @@ -212,7 +233,7 @@ func interceptor( // No need to change the context case version.VersionServer: ctx = log.WithModule(ctx, "version") - case snapshot.SnapshotsServer: + case snapshotapi.SnapshotsServer: ctx = log.WithModule(ctx, "snapshot") case diff.DiffServer: ctx = log.WithModule(ctx, "diff") diff --git a/services/content/service.go b/services/content/service.go index 6e01692da..6040793c2 100644 --- a/services/content/service.go +++ b/services/content/service.go @@ -39,7 +39,6 @@ func init() { Type: plugin.GRPCPlugin, ID: "content", Requires: []plugin.Type{ - plugin.ContentPlugin, plugin.MetadataPlugin, }, Init: NewService, @@ -47,19 +46,13 @@ func init() { } func NewService(ic *plugin.InitContext) (interface{}, error) { - c, err := ic.Get(plugin.ContentPlugin) - if err != nil { - return nil, err - } m, err := ic.Get(plugin.MetadataPlugin) if err != nil { return nil, err } - cs := metadata.NewContentStore(m.(*metadata.DB), c.(content.Store)) - return &Service{ - store: cs, + store: m.(*metadata.DB).ContentStore(), publisher: ic.Events, }, nil } diff --git a/services/snapshot/service.go b/services/snapshot/service.go index 581375f36..9d8b4cd79 100644 --- a/services/snapshot/service.go +++ b/services/snapshot/service.go @@ -14,7 +14,6 @@ import ( "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshot" protoempty "github.com/golang/protobuf/ptypes/empty" - "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/grpc" ) @@ -24,7 +23,6 @@ func init() { Type: plugin.GRPCPlugin, ID: "snapshots", Requires: []plugin.Type{ - plugin.SnapshotPlugin, plugin.MetadataPlugin, }, Init: newService, @@ -34,31 +32,19 @@ func init() { var empty = &protoempty.Empty{} type service struct { - snapshotters map[string]snapshot.Snapshotter - publisher events.Publisher + db *metadata.DB + publisher events.Publisher } func newService(ic *plugin.InitContext) (interface{}, error) { - rawSnapshotters, err := ic.GetAll(plugin.SnapshotPlugin) - if err != nil { - return nil, err - } md, err := ic.Get(plugin.MetadataPlugin) if err != nil { return nil, err } - snapshotters := make(map[string]snapshot.Snapshotter) - for name, sn := range rawSnapshotters { - snapshotters[name] = metadata.NewSnapshotter(md.(*metadata.DB), name, sn.(snapshot.Snapshotter)) - } - - if len(snapshotters) == 0 { - return nil, errors.Errorf("failed to create snapshotter service: no snapshotters loaded") - } return &service{ - snapshotters: snapshotters, - publisher: ic.Events, + db: md.(*metadata.DB), + publisher: ic.Events, }, nil } @@ -67,8 +53,8 @@ func (s *service) getSnapshotter(name string) (snapshot.Snapshotter, error) { return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter argument missing") } - sn, ok := s.snapshotters[name] - if !ok { + sn := s.db.Snapshotter(name) + if sn == nil { return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "snapshotter not loaded: %s", name) } return sn, nil diff --git a/services/tasks/service.go b/services/tasks/service.go index 84a04dc03..d619c6d65 100644 --- a/services/tasks/service.go +++ b/services/tasks/service.go @@ -44,7 +44,6 @@ func init() { Requires: []plugin.Type{ plugin.RuntimePlugin, plugin.MetadataPlugin, - plugin.ContentPlugin, }, Init: New, }) @@ -59,11 +58,7 @@ func New(ic *plugin.InitContext) (interface{}, error) { if err != nil { return nil, err } - ct, err := ic.Get(plugin.ContentPlugin) - if err != nil { - return nil, err - } - cs := metadata.NewContentStore(m.(*metadata.DB), ct.(content.Store)) + cs := m.(*metadata.DB).ContentStore() runtimes := make(map[string]runtime.Runtime) for _, rr := range rt { r := rr.(runtime.Runtime)