From a4247e2aa9f519f04d42879f5b474258ba34835a Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Tue, 7 Mar 2017 14:55:36 -0800 Subject: [PATCH] Add snapshot plugin type Update existing snapshot drivers to register as plugins. Load snapshot driver at containerd startup. Signed-off-by: Derek McGowan (github: dmcgowan) --- cmd/containerd/builtins.go | 2 ++ cmd/containerd/config.go | 3 +++ cmd/containerd/main.go | 53 ++++++++++++++++++++++++++++++++----- plugin/plugin.go | 15 ++++++----- snapshot/btrfs/btrfs.go | 21 ++++++++++++++- snapshot/overlay/overlay.go | 12 ++++++++- 6 files changed, 91 insertions(+), 15 deletions(-) diff --git a/cmd/containerd/builtins.go b/cmd/containerd/builtins.go index 258b6f3..4d151e7 100644 --- a/cmd/containerd/builtins.go +++ b/cmd/containerd/builtins.go @@ -5,4 +5,6 @@ import ( _ "github.com/docker/containerd/linux" _ "github.com/docker/containerd/services/content" _ "github.com/docker/containerd/services/execution" + _ "github.com/docker/containerd/snapshot/btrfs" + _ "github.com/docker/containerd/snapshot/overlay" ) diff --git a/cmd/containerd/config.go b/cmd/containerd/config.go index c091bc6..146a4a4 100644 --- a/cmd/containerd/config.go +++ b/cmd/containerd/config.go @@ -13,6 +13,7 @@ func defaultConfig() *config { Level: "info", Socket: "/run/containerd/debug.sock", }, + Snapshotter: "overlay", } } @@ -39,6 +40,8 @@ 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"` // Plugins provides plugin specific configuration for the initialization of a plugin Plugins map[string]toml.Primitive `toml:"plugins"` diff --git a/cmd/containerd/main.go b/cmd/containerd/main.go index d5c0721..b7224bc 100644 --- a/cmd/containerd/main.go +++ b/cmd/containerd/main.go @@ -21,6 +21,7 @@ import ( "github.com/docker/containerd/content" "github.com/docker/containerd/log" "github.com/docker/containerd/plugin" + "github.com/docker/containerd/snapshot" "github.com/docker/containerd/utils" metrics "github.com/docker/go-metrics" "github.com/pkg/errors" @@ -101,7 +102,11 @@ func main() { if err != nil { return err } - services, err := loadServices(runtimes, store) + snapshotter, err := loadSnapshotter(store) + if err != nil { + return err + } + services, err := loadServices(runtimes, store, snapshotter) if err != nil { return err } @@ -248,12 +253,45 @@ func loadRuntimes() (map[string]containerd.Runtime, error) { return o, nil } +func loadSnapshotter(store *content.Store) (snapshot.Snapshotter, error) { + for name, sr := range plugin.Registrations() { + if sr.Type != plugin.SnapshotPlugin { + continue + } + moduleName := fmt.Sprintf("snapshot-%s", conf.Snapshotter) + if name != moduleName { + continue + } + + log.G(global).Infof("loading snapshot plugin %q...", name) + ic := &plugin.InitContext{ + Root: conf.Root, + State: conf.State, + Store: store, + Context: log.WithModule(global, moduleName), + } + if sr.Config != nil { + if err := conf.decodePlugin(name, sr.Config); err != nil { + return nil, err + } + ic.Config = sr.Config + } + sn, err := sr.Init(ic) + if err != nil { + return nil, err + } + + return sn.(snapshot.Snapshotter), nil + } + return nil, fmt.Errorf("snapshotter not loaded: %v", conf.Snapshotter) +} + func newGRPCServer() *grpc.Server { s := grpc.NewServer(grpc.UnaryInterceptor(interceptor)) return s } -func loadServices(runtimes map[string]containerd.Runtime, store *content.Store) ([]plugin.Service, error) { +func loadServices(runtimes map[string]containerd.Runtime, store *content.Store, sn snapshot.Snapshotter) ([]plugin.Service, error) { var o []plugin.Service for name, sr := range plugin.Registrations() { if sr.Type != plugin.GRPCPlugin { @@ -261,11 +299,12 @@ func loadServices(runtimes map[string]containerd.Runtime, store *content.Store) } log.G(global).Infof("loading grpc service plugin %q...", name) ic := &plugin.InitContext{ - Root: conf.Root, - State: conf.State, - Context: log.WithModule(global, fmt.Sprintf("service-%s", name)), - Runtimes: runtimes, - Store: store, + Root: conf.Root, + State: conf.State, + Context: log.WithModule(global, fmt.Sprintf("service-%s", name)), + Runtimes: runtimes, + Store: store, + Snapshotter: sn, } if sr.Config != nil { if err := conf.decodePlugin(name, sr.Config); err != nil { diff --git a/plugin/plugin.go b/plugin/plugin.go index bbef1bf..de97e58 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -6,6 +6,7 @@ import ( "github.com/docker/containerd" "github.com/docker/containerd/content" + "github.com/docker/containerd/snapshot" "golang.org/x/net/context" "google.golang.org/grpc" @@ -16,6 +17,7 @@ type PluginType int const ( RuntimePlugin PluginType = iota + 1 GRPCPlugin + SnapshotPlugin ) type Registration struct { @@ -25,12 +27,13 @@ type Registration struct { } type InitContext struct { - Root string - State string - Runtimes map[string]containerd.Runtime - Store *content.Store - Config interface{} - Context context.Context + Root string + State string + Runtimes map[string]containerd.Runtime + Store *content.Store + Snapshotter snapshot.Snapshotter + Config interface{} + Context context.Context } type Service interface { diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go index 66481b9..bee93ac 100644 --- a/snapshot/btrfs/btrfs.go +++ b/snapshot/btrfs/btrfs.go @@ -10,17 +10,36 @@ import ( "strings" "github.com/docker/containerd" + "github.com/docker/containerd/plugin" "github.com/docker/containerd/snapshot" "github.com/pkg/errors" "github.com/stevvooe/go-btrfs" ) +type btrfsConfig struct { + Device string `toml:"device"` +} + +func init() { + plugin.Register("snapshot-btrfs", &plugin.Registration{ + Type: plugin.SnapshotPlugin, + Config: &btrfsConfig{}, + Init: func(ic *plugin.InitContext) (interface{}, error) { + conf := ic.Config.(*btrfsConfig) + if conf.Device == "" { + return nil, errors.Errorf("btrfs requires \"device\" configuration") + } + return NewSnapshotter(conf.Device, filepath.Join(ic.Root, "snapshot", "btrfs")) + }, + }) +} + type Snapshotter struct { device string // maybe we can resolve it with path? root string // root provides paths for internal storage. } -func NewSnapshotter(device, root string) (*Snapshotter, error) { +func NewSnapshotter(device, root string) (snapshot.Snapshotter, error) { var ( active = filepath.Join(root, "active") snapshots = filepath.Join(root, "snapshots") diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index d12770e..bc655e3 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -10,17 +10,27 @@ import ( "sync" "github.com/docker/containerd" + "github.com/docker/containerd/plugin" "github.com/docker/containerd/snapshot" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) +func init() { + plugin.Register("snapshot-overlay", &plugin.Registration{ + Type: plugin.SnapshotPlugin, + Init: func(ic *plugin.InitContext) (interface{}, error) { + return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "overlay")) + }, + }) +} + type Snapshotter struct { root string links *cache } -func NewSnapshotter(root string) (*Snapshotter, error) { +func NewSnapshotter(root string) (snapshot.Snapshotter, error) { if err := os.MkdirAll(root, 0700); err != nil { return nil, err }