From 3db8adc5d7f7f79ac1fc2f7c2ebb423a888575f3 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 27 Jun 2017 16:55:50 -0700 Subject: [PATCH] Update plugin load and snapshot service Allow plugins to be mapped and returned by their ID. Add skip plugin to allow plugins to decide whether they should be loaded. Signed-off-by: Derek McGowan --- cmd/containerd/config_linux.go | 2 -- cmd/containerd/config_unix.go | 2 -- cmd/containerd/config_windows.go | 2 -- plugin/context.go | 13 ++++----- plugin/plugin.go | 15 +++++++++- server/config.go | 5 ---- server/server.go | 32 ++++++++++------------ services/snapshot/default_linux.go | 5 ++++ services/snapshot/default_unix.go | 7 +++++ services/snapshot/default_windows.go | 5 ++++ services/snapshot/service.go | 41 +++++++++++++++++++++------- 11 files changed, 82 insertions(+), 47 deletions(-) create mode 100644 services/snapshot/default_linux.go create mode 100644 services/snapshot/default_unix.go create mode 100644 services/snapshot/default_windows.go diff --git a/cmd/containerd/config_linux.go b/cmd/containerd/config_linux.go index efb82309b..46956c524 100644 --- a/cmd/containerd/config_linux.go +++ b/cmd/containerd/config_linux.go @@ -15,7 +15,5 @@ func defaultConfig() *server.Config { Level: "info", Address: "/run/containerd/debug.sock", }, - Snapshotter: "io.containerd.snapshotter.v1.overlayfs", - Differ: "io.containerd.differ.v1.base-diff", } } diff --git a/cmd/containerd/config_unix.go b/cmd/containerd/config_unix.go index 287fd9ef2..da3b40f25 100644 --- a/cmd/containerd/config_unix.go +++ b/cmd/containerd/config_unix.go @@ -14,7 +14,5 @@ func defaultConfig() *server.Config { Level: "info", Address: "/run/containerd/debug.sock", }, - Snapshotter: "io.containerd.snapshotter.v1.naive", - Differ: "io.containerd.differ.v1.base-diff", } } diff --git a/cmd/containerd/config_windows.go b/cmd/containerd/config_windows.go index 4bf3b802e..628dd90f2 100644 --- a/cmd/containerd/config_windows.go +++ b/cmd/containerd/config_windows.go @@ -17,7 +17,5 @@ func defaultConfig() *server.Config { Level: "info", Address: `\\.\pipe\containerd-debug`, }, - Snapshotter: "io.containerd.snapshotter.v1.windows", - Differ: "io.containerd.differ.v1.base-diff", } } diff --git a/plugin/context.go b/plugin/context.go index 6e6d0fc9c..7b84745bd 100644 --- a/plugin/context.go +++ b/plugin/context.go @@ -9,7 +9,7 @@ import ( "github.com/containerd/containerd/log" ) -func NewContext(ctx context.Context, plugins map[PluginType][]interface{}, root, id string) *InitContext { +func NewContext(ctx context.Context, plugins map[PluginType]map[string]interface{}, root, id string) *InitContext { return &InitContext{ plugins: plugins, Root: filepath.Join(root, id), @@ -23,18 +23,17 @@ type InitContext struct { Config interface{} Emitter *events.Emitter - plugins map[PluginType][]interface{} + plugins map[PluginType]map[string]interface{} } func (i *InitContext) Get(t PluginType) (interface{}, error) { - p := i.plugins[t] - if len(p) == 0 { - return nil, fmt.Errorf("no plugins registered for %s", t) + for _, v := range i.plugins[t] { + return v, nil } - return p[0], nil + return nil, fmt.Errorf("no plugins registered for %s", t) } -func (i *InitContext) GetAll(t PluginType) ([]interface{}, error) { +func (i *InitContext) GetAll(t PluginType) (map[string]interface{}, error) { p, ok := i.plugins[t] if !ok { return nil, fmt.Errorf("no plugins registered for %s", t) diff --git a/plugin/plugin.go b/plugin/plugin.go index 14ee9cf35..7f8461790 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -1,18 +1,31 @@ package plugin import ( - "errors" "fmt" "sync" + "github.com/pkg/errors" "google.golang.org/grpc" ) var ( ErrNoPluginType = errors.New("plugin: no type") ErrNoPluginID = errors.New("plugin: no id") + + // SkipPlugin is used when a plugin is not initialized and should not be loaded, + // this allows the plugin loader differentiate between a plugin which is configured + // not to load and one that fails to load. + SkipPlugin = errors.New("skip plugin") ) +// IsSkipPlugin returns true if the error is skipping the plugin +func IsSkipPlugin(err error) bool { + if errors.Cause(err) == SkipPlugin { + return true + } + return false +} + type PluginType string const ( diff --git a/server/config.go b/server/config.go index 3fa53dea1..02659b669 100644 --- a/server/config.go +++ b/server/config.go @@ -17,11 +17,6 @@ type Config struct { Debug Debug `toml:"debug"` // Metrics and monitoring settings Metrics MetricsConfig `toml:"metrics"` - // Snapshotter specifies which snapshot driver to use - Snapshotter string `toml:"snapshotter"` - // Differ specifies which differ to use. Differ is tightly coupled with the snapshotter - // so not all combinations may work. - Differ string `toml:"differ"` // Plugins provides plugin specific configuration for the initialization of a plugin Plugins map[string]toml.Primitive `toml:"plugins"` // Enable containerd as a subreaper diff --git a/server/server.go b/server/server.go index d91780e6e..4225f9439 100644 --- a/server/server.go +++ b/server/server.go @@ -55,14 +55,10 @@ func New(ctx context.Context, config *Config) (*Server, error) { rpc: rpc, emitter: events.NewEmitter(), } - initialized = make(map[plugin.PluginType][]interface{}) + initialized = make(map[plugin.PluginType]map[string]interface{}) ) for _, p := range plugins { id := p.URI() - if !shouldLoadPlugin(p, config) { - log.G(ctx).WithField("type", p.Type).Infof("skip loading plugin %q...", id) - continue - } log.G(ctx).WithField("type", p.Type).Infof("loading plugin %q...", id) initContext := plugin.NewContext( @@ -83,10 +79,21 @@ func New(ctx context.Context, config *Config) (*Server, error) { } instance, err := p.Init(initContext) if err != nil { - log.G(ctx).WithError(err).Warnf("failed to load plugin %s", id) + if plugin.IsSkipPlugin(err) { + log.G(ctx).WithField("type", p.Type).Infof("skip loading plugin %q...", id) + } else { + log.G(ctx).WithError(err).Warnf("failed to load plugin %s", id) + } continue } - initialized[p.Type] = append(initialized[p.Type], instance) + + if types, ok := initialized[p.Type]; ok { + types[p.ID] = instance + } else { + initialized[p.Type] = map[string]interface{}{ + p.ID: instance, + } + } // check for grpc services that should be registered with the server if service, ok := instance.(plugin.Service); ok { services = append(services, service) @@ -170,17 +177,6 @@ func loadPlugins(config *Config) ([]*plugin.Registration, error) { return plugin.Graph(), nil } -func shouldLoadPlugin(p *plugin.Registration, config *Config) bool { - switch p.Type { - case plugin.SnapshotPlugin: - return p.URI() == config.Snapshotter - case plugin.DiffPlugin: - return p.URI() == config.Differ - default: - return true - } -} - func interceptor( ctx context.Context, req interface{}, diff --git a/services/snapshot/default_linux.go b/services/snapshot/default_linux.go new file mode 100644 index 000000000..13f75af41 --- /dev/null +++ b/services/snapshot/default_linux.go @@ -0,0 +1,5 @@ +package snapshot + +const ( + defaultSnapshotter = "overlayfs" +) diff --git a/services/snapshot/default_unix.go b/services/snapshot/default_unix.go new file mode 100644 index 000000000..1adf5ca7d --- /dev/null +++ b/services/snapshot/default_unix.go @@ -0,0 +1,7 @@ +// +build darwin freebsd + +package snapshot + +const ( + defaultSnapshotter = "naive" +) diff --git a/services/snapshot/default_windows.go b/services/snapshot/default_windows.go new file mode 100644 index 000000000..680631e32 --- /dev/null +++ b/services/snapshot/default_windows.go @@ -0,0 +1,5 @@ +package snapshot + +const ( + defaultSnapshotter = "windows" +) diff --git a/services/snapshot/service.go b/services/snapshot/service.go index c1d9f2ffe..a42fa7211 100644 --- a/services/snapshot/service.go +++ b/services/snapshot/service.go @@ -3,36 +3,40 @@ package snapshot import ( gocontext "context" + "github.com/boltdb/bolt" eventsapi "github.com/containerd/containerd/api/services/events/v1" snapshotapi "github.com/containerd/containerd/api/services/snapshot/v1" "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/events" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/snapshot" - "github.com/containerd/containerd/snapshot/namespaced" protoempty "github.com/golang/protobuf/ptypes/empty" + "github.com/pkg/errors" "golang.org/x/net/context" "google.golang.org/grpc" ) +type config struct { + // Default is the default snapshotter to use for the service + Default string `toml:"default,omitempty"` +} + func init() { plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, ID: "snapshots", Requires: []plugin.PluginType{ plugin.SnapshotPlugin, + plugin.MetadataPlugin, }, - Init: func(ic *plugin.InitContext) (interface{}, error) { - e := events.GetPoster(ic.Context) - s, err := ic.Get(plugin.SnapshotPlugin) - if err != nil { - return nil, err - } - return newService(s.(snapshot.Snapshotter), e) + Config: &config{ + Default: defaultSnapshotter, }, + Init: newService, }) } @@ -43,9 +47,26 @@ type service struct { emitter events.Poster } -func newService(snapshotter snapshot.Snapshotter, evts events.Poster) (*service, error) { +func newService(ic *plugin.InitContext) (interface{}, error) { + evts := events.GetPoster(ic.Context) + snapshotters, err := ic.GetAll(plugin.SnapshotPlugin) + if err != nil { + return nil, err + } + cfg := ic.Config.(*config) + + sn, ok := snapshotters[cfg.Default] + if !ok { + return nil, errors.Errorf("default snapshotter not loaded: %s", cfg.Default) + } + + md, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + return &service{ - snapshotter: namespaced.NewSnapshotter(snapshotter), + snapshotter: metadata.NewSnapshotter(md.(*bolt.DB), cfg.Default, sn.(snapshot.Snapshotter)), emitter: evts, }, nil }