From 94e7f8e943720ae5922f8184e0310077d7ed5f54 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 8 Jun 2017 09:45:22 -0700 Subject: [PATCH] Setup plugin ids and dependencies Signed-off-by: Michael Crosby --- client.go | 4 +- cmd/containerd/config.go | 2 - cmd/containerd/config_linux.go | 7 +- cmd/containerd/config_unix.go | 7 +- cmd/containerd/config_windows.go | 7 +- cmd/containerd/main.go | 265 ++++++++----------------------- cmd/containerd/main_linux.go | 19 +-- cmd/containerd/main_unix.go | 6 - cmd/ctr/run.go | 4 +- differ/differ.go | 17 +- linux/runtime.go | 28 ++-- linux/task.go | 2 +- metrics/cgroups/cgroups.go | 5 +- plugin/context.go | 36 +++++ plugin/errors.go | 1 - plugin/plugin.go | 92 ++++++----- plugin/runtime.go | 2 + services/containers/service.go | 12 +- services/content/service.go | 12 +- services/diff/service.go | 12 +- services/execution/service.go | 35 +++- services/healthcheck/service.go | 3 +- services/images/service.go | 12 +- services/namespaces/service.go | 12 +- services/snapshot/service.go | 12 +- services/version/service.go | 3 +- snapshot/btrfs/btrfs.go | 6 +- snapshot/naive/naive.go | 5 +- snapshot/overlay/overlay.go | 5 +- snapshot/windows/windows.go | 6 +- windows/runtime.go | 11 +- 31 files changed, 331 insertions(+), 319 deletions(-) create mode 100644 plugin/context.go diff --git a/client.go b/client.go index f017337a5..a1ba9b825 100644 --- a/client.go +++ b/client.go @@ -2,6 +2,7 @@ package containerd import ( "context" + "fmt" "io/ioutil" "log" "net/http" @@ -19,6 +20,7 @@ import ( versionservice "github.com/containerd/containerd/api/services/version" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" + "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" contentservice "github.com/containerd/containerd/services/content" @@ -83,7 +85,7 @@ func New(address string, opts ...ClientOpt) (*Client, error) { } return &Client{ conn: conn, - runtime: runtime.GOOS, + runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS), }, nil } diff --git a/cmd/containerd/config.go b/cmd/containerd/config.go index 1a451add0..ebeddb7cd 100644 --- a/cmd/containerd/config.go +++ b/cmd/containerd/config.go @@ -37,8 +37,6 @@ func loadConfig(path string) error { // config specifies the containerd configuration file in the TOML format. // It contains fields to configure various subsystems and containerd as a whole. type config struct { - // State is the path to a directory where containerd will store runtime state - State string `toml:"state"` // Root is the path to a directory where containerd will store persistent data Root string `toml:"root"` // GRPC configuration settings diff --git a/cmd/containerd/config_linux.go b/cmd/containerd/config_linux.go index f88ed0266..b95385ce5 100644 --- a/cmd/containerd/config_linux.go +++ b/cmd/containerd/config_linux.go @@ -2,8 +2,7 @@ package main func defaultConfig() *config { return &config{ - Root: "/var/lib/containerd", - State: "/run/containerd", + Root: "/var/lib/containerd", GRPC: grpcConfig{ Address: "/run/containerd/containerd.sock", }, @@ -11,7 +10,7 @@ func defaultConfig() *config { Level: "info", Address: "/run/containerd/debug.sock", }, - Snapshotter: "overlay", - Differ: "base", + 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 e26b5f821..a96085459 100644 --- a/cmd/containerd/config_unix.go +++ b/cmd/containerd/config_unix.go @@ -4,8 +4,7 @@ package main func defaultConfig() *config { return &config{ - Root: "/var/lib/containerd", - State: "/run/containerd", + Root: "/var/lib/containerd", GRPC: grpcConfig{ Address: "/run/containerd/containerd.sock", }, @@ -13,7 +12,7 @@ func defaultConfig() *config { Level: "info", Address: "/run/containerd/debug.sock", }, - Snapshotter: "naive", - Differ: "base", + 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 dbeabe3fd..4c11627cd 100644 --- a/cmd/containerd/config_windows.go +++ b/cmd/containerd/config_windows.go @@ -7,8 +7,7 @@ import ( func defaultConfig() *config { return &config{ - Root: filepath.Join(os.Getenv("programfiles"), "containerd", "root"), - State: filepath.Join(os.Getenv("programfiles"), "containerd", "state"), + Root: filepath.Join(os.Getenv("programfiles"), "containerd", "root"), GRPC: grpcConfig{ Address: `\\.\pipe\containerd-containerd`, }, @@ -16,7 +15,7 @@ func defaultConfig() *config { Level: "info", Address: `\\.\pipe\containerd-debug`, }, - Snapshotter: "windows", - Differ: "base", + Snapshotter: "io.containerd.snapshotter.v1.windows", + Differ: "io.containerd.differ.v1.base-diff", } } diff --git a/cmd/containerd/main.go b/cmd/containerd/main.go index e433156cf..095d291dd 100644 --- a/cmd/containerd/main.go +++ b/cmd/containerd/main.go @@ -29,7 +29,6 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/log" "github.com/containerd/containerd/plugin" - "github.com/containerd/containerd/snapshot" "github.com/containerd/containerd/sys" "github.com/containerd/containerd/version" metrics "github.com/docker/go-metrics" @@ -105,41 +104,42 @@ func main() { if err := plugin.Load(filepath.Join(conf.Root, "plugins")); err != nil { return err } + registerContentStore() + registerMetaDB() // start debug and metrics APIs if err := serveDebugAPI(); err != nil { return err } - monitor, err := loadMonitor() - if err != nil { - return err - } - runtimes, err := loadRuntimes(monitor) - if err != nil { - return err - } - store, err := resolveContentStore() - if err != nil { - return err - } - meta, err := resolveMetaDB(context) - if err != nil { - return err - } - defer meta.Close() - snapshotter, err := loadSnapshotter(store) - if err != nil { - return err + + var ( + services []plugin.Service + plugins = make(map[plugin.PluginType][]interface{}) + ) + for _, init := range plugin.Graph() { + id := init.URI() + log.G(global).WithField("type", init.Type).Infof("loading plugin %q...", id) + if !shouldLoad(init) { + continue + } + ic := plugin.NewContext(plugins) + ic.Root = filepath.Join(conf.Root, id) + ic.Context = log.WithModule(global, id) + if init.Config != nil { + if err := loadPluginConfig(init.ID, init.Config, ic); err != nil { + return err + } + } + + p, err := init.Init(ic) + if err != nil { + return err + } + plugins[init.Type] = append(plugins[init.Type], p) + if s, ok := p.(plugin.Service); ok { + services = append(services, s) + } } - differ, err := loadDiffer(snapshotter, store) - if err != nil { - return err - } - - services, err := loadServices(runtimes, store, snapshotter, meta, differ) - if err != nil { - return err - } // start the GRPC api with the execution service registered server := newGRPCServer() for _, service := range services { @@ -159,7 +159,6 @@ func main() { log.G(global).Infof("containerd successfully booted in %fs", time.Since(start).Seconds()) return handleSignals(signals, server) } - if err := app.Run(os.Args); err != nil { fmt.Fprintf(os.Stderr, "containerd: %s\n", err) os.Exit(1) @@ -184,10 +183,6 @@ func before(context *cli.Context) error { name: "root", d: &conf.Root, }, - { - name: "state", - d: &conf.State, - }, { name: "address", d: &conf.GRPC.Address, @@ -254,139 +249,27 @@ func serveDebugAPI() error { return nil } -func resolveContentStore() (content.Store, error) { - cp := filepath.Join(conf.Root, "content") - return content.NewStore(cp) +func registerContentStore() { + plugin.Register(&plugin.Registration{ + Type: plugin.ContentPlugin, + ID: "content", + Init: func(ic *plugin.InitContext) (interface{}, error) { + return content.NewStore(ic.Root) + }, + }) } -func resolveMetaDB(ctx *cli.Context) (*bolt.DB, error) { - path := filepath.Join(conf.Root, "meta.db") - - db, err := bolt.Open(path, 0644, nil) - if err != nil { - return nil, err - } - - return db, nil -} - -func loadRuntimes(monitor plugin.TaskMonitor) (map[string]plugin.Runtime, error) { - o := make(map[string]plugin.Runtime) - for name, rr := range plugin.Registrations() { - if rr.Type != plugin.RuntimePlugin { - continue - } - log.G(global).Infof("loading runtime plugin %q...", name) - ic := &plugin.InitContext{ - Root: conf.Root, - State: conf.State, - Context: log.WithModule(global, fmt.Sprintf("runtime-%s", name)), - Monitor: monitor, - } - if rr.Config != nil { - if err := conf.decodePlugin(name, rr.Config); err != nil { +func registerMetaDB() { + plugin.Register(&plugin.Registration{ + Type: plugin.MetadataPlugin, + ID: "bolt", + Init: func(ic *plugin.InitContext) (interface{}, error) { + if err := os.MkdirAll(ic.Root, 0700); err != nil { return nil, err } - ic.Config = rr.Config - } - vr, err := rr.Init(ic) - if err != nil { - return nil, err - } - o[name] = vr.(plugin.Runtime) - } - return o, nil -} - -func loadMonitor() (plugin.TaskMonitor, error) { - var monitors []plugin.TaskMonitor - for name, m := range plugin.Registrations() { - if m.Type != plugin.TaskMonitorPlugin { - continue - } - log.G(global).Infof("loading monitor plugin %q...", name) - ic := &plugin.InitContext{ - Root: conf.Root, - State: conf.State, - Context: log.WithModule(global, fmt.Sprintf("monitor-%s", name)), - } - mm, err := m.Init(ic) - if err != nil { - return nil, err - } - monitors = append(monitors, mm.(plugin.TaskMonitor)) - } - if len(monitors) == 0 { - return plugin.NewNoopMonitor(), nil - } - return plugin.NewMultiTaskMonitor(monitors...), 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, - Content: 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 loadDiffer(snapshotter snapshot.Snapshotter, store content.Store) (plugin.Differ, error) { - for name, sr := range plugin.Registrations() { - if sr.Type != plugin.DiffPlugin { - continue - } - moduleName := fmt.Sprintf("diff-%s", conf.Differ) - if name != moduleName { - continue - } - - log.G(global).Infof("loading differ plugin %q...", name) - ic := &plugin.InitContext{ - Root: conf.Root, - State: conf.State, - Content: store, - Snapshotter: snapshotter, - 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.(plugin.Differ), nil - } - return nil, fmt.Errorf("differ not loaded: %v", conf.Differ) + return bolt.Open(filepath.Join(ic.Root, "meta.db"), 0644, nil) + }, + }) } func newGRPCServer() *grpc.Server { @@ -397,40 +280,6 @@ func newGRPCServer() *grpc.Server { return s } -func loadServices(runtimes map[string]plugin.Runtime, - store content.Store, sn snapshot.Snapshotter, - meta *bolt.DB, differ plugin.Differ) ([]plugin.Service, error) { - var o []plugin.Service - for name, sr := range plugin.Registrations() { - if sr.Type != plugin.GRPCPlugin { - continue - } - 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, - Content: store, - Meta: meta, - Snapshotter: sn, - Differ: differ, - } - if sr.Config != nil { - if err := conf.decodePlugin(name, sr.Config); err != nil { - return nil, err - } - ic.Config = sr.Config - } - vs, err := sr.Init(ic) - if err != nil { - return nil, err - } - o = append(o, vs.(plugin.Service)) - } - return o, nil -} - func serveGRPC(server *grpc.Server) error { path := conf.GRPC.Address if path == "" { @@ -494,3 +343,23 @@ func dumpStacks() { buf = buf[:stackSize] logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) } + +func loadPluginConfig(name string, c interface{}, ic *plugin.InitContext) error { + if err := conf.decodePlugin(name, c); err != nil { + return err + } + ic.Config = c + return nil +} + +func shouldLoad(r *plugin.Registration) bool { + // only load certain plugins based on the config values + switch r.Type { + case plugin.SnapshotPlugin: + return r.URI() == conf.Snapshotter + case plugin.DiffPlugin: + return r.URI() == conf.Differ + default: + return true + } +} diff --git a/cmd/containerd/main_linux.go b/cmd/containerd/main_linux.go index 91558dbe8..440b3745b 100644 --- a/cmd/containerd/main_linux.go +++ b/cmd/containerd/main_linux.go @@ -12,13 +12,14 @@ import ( "google.golang.org/grpc" ) -const ( - defaultConfigPath = "/etc/containerd/config.toml" -) +const defaultConfigPath = "/etc/containerd/config.toml" -var ( - handledSignals = []os.Signal{unix.SIGTERM, unix.SIGINT, unix.SIGUSR1, unix.SIGCHLD} -) +var handledSignals = []os.Signal{ + unix.SIGTERM, + unix.SIGINT, + unix.SIGUSR1, + unix.SIGCHLD, +} func platformInit(context *cli.Context) error { if conf.Subreaper { @@ -33,12 +34,6 @@ func platformInit(context *cli.Context) error { return err } } - if err := os.MkdirAll(conf.State, 0750); err != nil { - return err - } - if err := os.Chown(conf.State, conf.GRPC.Uid, conf.GRPC.Gid); err != nil { - return err - } return nil } diff --git a/cmd/containerd/main_unix.go b/cmd/containerd/main_unix.go index 3059244e9..d82139dbd 100644 --- a/cmd/containerd/main_unix.go +++ b/cmd/containerd/main_unix.go @@ -22,12 +22,6 @@ var ( ) func platformInit(context *cli.Context) error { - if err := os.MkdirAll(conf.State, 0750); err != nil { - return err - } - if err := os.Chown(conf.State, conf.GRPC.Uid, conf.GRPC.Gid); err != nil { - return err - } return nil } diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index cac694964..fb52ed45d 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -55,8 +55,8 @@ var runCommand = cli.Command{ }, cli.StringFlag{ Name: "runtime", - Usage: "runtime name (linux, windows, vmware-linux)", - Value: "linux", + Usage: "runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux)", + Value: "io.containerd.runtime.v1.linux", }, cli.BoolFlag{ Name: "readonly", diff --git a/differ/differ.go b/differ/differ.go index 10883df4e..047c5a60c 100644 --- a/differ/differ.go +++ b/differ/differ.go @@ -18,10 +18,23 @@ import ( ) func init() { - plugin.Register("diff-base", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.DiffPlugin, + ID: "base-diff", + Requires: []plugin.PluginType{ + plugin.ContentPlugin, + plugin.SnapshotPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { - return newBaseDiff(ic.Content, ic.Snapshotter) + c, err := ic.Get(plugin.ContentPlugin) + if err != nil { + return nil, err + } + s, err := ic.Get(plugin.SnapshotPlugin) + if err != nil { + return nil, err + } + return newBaseDiff(c.(content.Store), s.(snapshot.Snapshotter)) }, }) } diff --git a/linux/runtime.go b/linux/runtime.go index 81ad9837d..d47845637 100644 --- a/linux/runtime.go +++ b/linux/runtime.go @@ -32,19 +32,23 @@ import ( var ( ErrTaskNotExists = errors.New("task does not exist") ErrTaskAlreadyExists = errors.New("task already exists") + pluginID = fmt.Sprintf("%s.%s", plugin.RuntimePlugin, "linux") ) const ( - runtimeName = "linux" configFilename = "config.json" defaultRuntime = "runc" defaultShim = "containerd-shim" ) func init() { - plugin.Register(runtimeName, &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.RuntimePlugin, + ID: "linux", Init: New, + Requires: []plugin.PluginType{ + plugin.TaskMonitorPlugin, + }, Config: &Config{ Shim: defaultShim, Runtime: defaultRuntime, @@ -129,25 +133,28 @@ func (l *taskList) delete(ctx context.Context, t *Task) { } func New(ic *plugin.InitContext) (interface{}, error) { - path := filepath.Join(ic.State, runtimeName) - if err := os.MkdirAll(path, 0700); err != nil { + if err := os.MkdirAll(ic.Root, 0700); err != nil { + return nil, err + } + monitor, err := ic.Get(plugin.TaskMonitorPlugin) + if err != nil { return nil, err } cfg := ic.Config.(*Config) c, cancel := context.WithCancel(ic.Context) r := &Runtime{ - root: path, + root: ic.Root, remote: !cfg.NoShim, shim: cfg.Shim, runtime: cfg.Runtime, events: make(chan *plugin.Event, 2048), eventsContext: c, eventsCancel: cancel, - monitor: ic.Monitor, + monitor: monitor.(plugin.TaskMonitor), tasks: newTaskList(), } // set the events output for a monitor if it generates events - ic.Monitor.Events(r.events) + r.monitor.Events(r.events) tasks, err := r.loadAllTasks(ic.Context) if err != nil { return nil, err @@ -157,7 +164,6 @@ func New(ic *plugin.InitContext) (interface{}, error) { return nil, err } } - // load all tasks from disk return r, nil } @@ -174,6 +180,10 @@ type Runtime struct { tasks *taskList } +func (r *Runtime) ID() string { + return pluginID +} + func (r *Runtime) Create(ctx context.Context, id string, opts plugin.CreateOpts) (t plugin.Task, err error) { namespace, err := namespaces.NamespaceRequired(ctx) if err != nil { @@ -358,7 +368,7 @@ func (r *Runtime) forward(events shim.Shim_EventsClient) { } r.events <- &plugin.Event{ Timestamp: time.Now(), - Runtime: runtimeName, + Runtime: r.ID(), Type: et, Pid: e.Pid, ID: e.ID, diff --git a/linux/task.go b/linux/task.go index b2ed95641..d2c160b3f 100644 --- a/linux/task.go +++ b/linux/task.go @@ -35,7 +35,7 @@ func (c *Task) Info() plugin.TaskInfo { return plugin.TaskInfo{ ID: c.containerID, ContainerID: c.containerID, - Runtime: runtimeName, + Runtime: pluginID, Spec: c.spec, Namespace: c.namespace, } diff --git a/metrics/cgroups/cgroups.go b/metrics/cgroups/cgroups.go index 6d1750205..a2e33891c 100644 --- a/metrics/cgroups/cgroups.go +++ b/metrics/cgroups/cgroups.go @@ -13,11 +13,10 @@ import ( "golang.org/x/net/context" ) -const name = "cgroups" - func init() { - plugin.Register(name, &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.TaskMonitorPlugin, + ID: "cgroups", Init: New, }) } diff --git a/plugin/context.go b/plugin/context.go new file mode 100644 index 000000000..9f19dffa7 --- /dev/null +++ b/plugin/context.go @@ -0,0 +1,36 @@ +package plugin + +import ( + "context" + "fmt" +) + +func NewContext(plugins map[PluginType][]interface{}) *InitContext { + return &InitContext{ + plugins: plugins, + } +} + +type InitContext struct { + Root string + Context context.Context + Config interface{} + + plugins map[PluginType][]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) + } + return p[0], nil +} + +func (i *InitContext) GetAll(t PluginType) ([]interface{}, error) { + p, ok := i.plugins[t] + if !ok { + return nil, fmt.Errorf("no plugins registered for %s", t) + } + return p, nil +} diff --git a/plugin/errors.go b/plugin/errors.go index 60d5203ee..ac2003ead 100644 --- a/plugin/errors.go +++ b/plugin/errors.go @@ -3,7 +3,6 @@ package plugin import "errors" var ( - ErrUnknownRuntime = errors.New("unknown runtime") ErrContainerExists = errors.New("container with id already exists") ErrContainerNotExist = errors.New("container does not exist") ErrRuntimeNotExist = errors.New("runtime does not exist") diff --git a/plugin/plugin.go b/plugin/plugin.go index 3aa2eab83..14ee9cf35 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -1,45 +1,42 @@ package plugin import ( + "errors" "fmt" "sync" - "github.com/boltdb/bolt" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/snapshot" - - "golang.org/x/net/context" "google.golang.org/grpc" ) -type PluginType int +var ( + ErrNoPluginType = errors.New("plugin: no type") + ErrNoPluginID = errors.New("plugin: no id") +) + +type PluginType string const ( - RuntimePlugin PluginType = iota + 1 - GRPCPlugin - SnapshotPlugin - TaskMonitorPlugin - DiffPlugin + RuntimePlugin PluginType = "io.containerd.runtime.v1" + GRPCPlugin PluginType = "io.containerd.grpc.v1" + SnapshotPlugin PluginType = "io.containerd.snapshotter.v1" + TaskMonitorPlugin PluginType = "io.containerd.monitor.v1" + DiffPlugin PluginType = "io.containerd.differ.v1" + MetadataPlugin PluginType = "io.containerd.metadata.v1" + ContentPlugin PluginType = "io.containerd.content.v1" ) type Registration struct { - Type PluginType - Config interface{} - Init func(*InitContext) (interface{}, error) + Type PluginType + ID string + Config interface{} + Requires []PluginType + Init func(*InitContext) (interface{}, error) + + added bool } -// TODO(@crosbymichael): how do we keep this struct from growing but support dependency injection for loaded plugins? -type InitContext struct { - Root string - State string - Runtimes map[string]Runtime - Content content.Store - Meta *bolt.DB - Snapshotter snapshot.Snapshotter - Differ Differ - Config interface{} - Context context.Context - Monitor TaskMonitor +func (r *Registration) URI() string { + return fmt.Sprintf("%s.%s", r.Type, r.ID) } type Service interface { @@ -48,10 +45,8 @@ type Service interface { var register = struct { sync.Mutex - r map[string]*Registration -}{ - r: make(map[string]*Registration), -} + r []*Registration +}{} // Load loads all plugins at the provided path into containerd func Load(path string) (err error) { @@ -67,16 +62,39 @@ func Load(path string) (err error) { return loadPlugins(path) } -func Register(name string, r *Registration) error { +func Register(r *Registration) { register.Lock() defer register.Unlock() - if _, ok := register.r[name]; ok { - return fmt.Errorf("plugin already registered as %q", name) + if r.Type == "" { + panic(ErrNoPluginType) } - register.r[name] = r - return nil + if r.ID == "" { + panic(ErrNoPluginID) + } + register.r = append(register.r, r) } -func Registrations() map[string]*Registration { - return register.r +func Graph() (ordered []*Registration) { + for _, r := range register.r { + children(r.Requires, &ordered) + if !r.added { + ordered = append(ordered, r) + r.added = true + } + } + return ordered +} + +func children(types []PluginType, ordered *[]*Registration) { + for _, t := range types { + for _, r := range register.r { + if r.Type == t { + children(r.Requires, ordered) + if !r.added { + *ordered = append(*ordered, r) + r.added = true + } + } + } + } } diff --git a/plugin/runtime.go b/plugin/runtime.go index 2fba9faef..8c38a5eb1 100644 --- a/plugin/runtime.go +++ b/plugin/runtime.go @@ -32,6 +32,8 @@ type Exit struct { // Runtime is responsible for the creation of containers for a certain platform, // arch, or custom usage. type Runtime interface { + // ID of the runtime + ID() string // Create creates a container with the provided id and options Create(ctx context.Context, id string, opts CreateOpts) (Task, error) // Get returns a container diff --git a/services/containers/service.go b/services/containers/service.go index 7de0bbc18..7b23ee1d1 100644 --- a/services/containers/service.go +++ b/services/containers/service.go @@ -13,10 +13,18 @@ import ( ) func init() { - plugin.Register("containers-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "containers", + Requires: []plugin.PluginType{ + plugin.MetadataPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewService(ic.Meta), nil + m, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + return NewService(m.(*bolt.DB)), nil }, }) } diff --git a/services/content/service.go b/services/content/service.go index 34b981bdd..fb84d113c 100644 --- a/services/content/service.go +++ b/services/content/service.go @@ -30,15 +30,23 @@ var bufPool = sync.Pool{ var _ api.ContentServer = &Service{} func init() { - plugin.Register("content-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "content", + Requires: []plugin.PluginType{ + plugin.ContentPlugin, + }, Init: NewService, }) } func NewService(ic *plugin.InitContext) (interface{}, error) { + c, err := ic.Get(plugin.ContentPlugin) + if err != nil { + return nil, err + } return &Service{ - store: ic.Content, + store: c.(content.Store), }, nil } diff --git a/services/diff/service.go b/services/diff/service.go index 79da2ba5c..c132575e9 100644 --- a/services/diff/service.go +++ b/services/diff/service.go @@ -10,11 +10,19 @@ import ( ) func init() { - plugin.Register("diff-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "diff", + Requires: []plugin.PluginType{ + plugin.DiffPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { + d, err := ic.Get(plugin.DiffPlugin) + if err != nil { + return nil, err + } return &service{ - diff: ic.Differ, + diff: d.(plugin.Differ), }, nil }, }) diff --git a/services/execution/service.go b/services/execution/service.go index a688e08f1..3235e63d0 100644 --- a/services/execution/service.go +++ b/services/execution/service.go @@ -35,22 +35,45 @@ var ( ) func init() { - plugin.Register("tasks-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "tasks", + Requires: []plugin.PluginType{ + plugin.RuntimePlugin, + plugin.MetadataPlugin, + plugin.ContentPlugin, + }, Init: New, }) } func New(ic *plugin.InitContext) (interface{}, error) { - c, err := newCollector(ic.Context, ic.Runtimes) + rt, err := ic.GetAll(plugin.RuntimePlugin) + if err != nil { + return nil, err + } + m, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + ct, err := ic.Get(plugin.ContentPlugin) + if err != nil { + return nil, err + } + runtimes := make(map[string]plugin.Runtime) + for _, rr := range rt { + r := rr.(plugin.Runtime) + runtimes[r.ID()] = r + } + c, err := newCollector(ic.Context, runtimes) if err != nil { return nil, err } return &Service{ - runtimes: ic.Runtimes, - db: ic.Meta, + runtimes: runtimes, + db: m.(*bolt.DB), collector: c, - store: ic.Content, + store: ct.(content.Store), }, nil } @@ -461,7 +484,7 @@ func (s *Service) getTask(ctx context.Context, id string) (plugin.Task, error) { func (s *Service) getRuntime(name string) (plugin.Runtime, error) { runtime, ok := s.runtimes[name] if !ok { - return nil, plugin.ErrUnknownRuntime + return nil, fmt.Errorf("unknown runtime %q", name) } return runtime, nil } diff --git a/services/healthcheck/service.go b/services/healthcheck/service.go index 4202c1592..5de7a0544 100644 --- a/services/healthcheck/service.go +++ b/services/healthcheck/service.go @@ -13,8 +13,9 @@ type Service struct { } func init() { - plugin.Register("healthcheck-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "healthcheck", Init: NewService, }) } diff --git a/services/images/service.go b/services/images/service.go index f6643637e..57d1f0b14 100644 --- a/services/images/service.go +++ b/services/images/service.go @@ -12,10 +12,18 @@ import ( ) func init() { - plugin.Register("images-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "images", + Requires: []plugin.PluginType{ + plugin.MetadataPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewService(ic.Meta), nil + m, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + return NewService(m.(*bolt.DB)), nil }, }) } diff --git a/services/namespaces/service.go b/services/namespaces/service.go index 4a5388e4d..03c0a786c 100644 --- a/services/namespaces/service.go +++ b/services/namespaces/service.go @@ -15,10 +15,18 @@ import ( ) func init() { - plugin.Register("namespaces-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "namespaces", + Requires: []plugin.PluginType{ + plugin.MetadataPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewService(ic.Meta), nil + m, err := ic.Get(plugin.MetadataPlugin) + if err != nil { + return nil, err + } + return NewService(m.(*bolt.DB)), nil }, }) } diff --git a/services/snapshot/service.go b/services/snapshot/service.go index 6986bf22b..f4baf7257 100644 --- a/services/snapshot/service.go +++ b/services/snapshot/service.go @@ -16,10 +16,18 @@ import ( ) func init() { - plugin.Register("snapshots-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "snapshots", + Requires: []plugin.PluginType{ + plugin.SnapshotPlugin, + }, Init: func(ic *plugin.InitContext) (interface{}, error) { - return newService(ic.Snapshotter) + s, err := ic.Get(plugin.SnapshotPlugin) + if err != nil { + return nil, err + } + return newService(s.(snapshot.Snapshotter)) }, }) } diff --git a/services/version/service.go b/services/version/service.go index 7df3d4ae2..09a2bf3a1 100644 --- a/services/version/service.go +++ b/services/version/service.go @@ -12,8 +12,9 @@ import ( var _ api.VersionServer = &Service{} func init() { - plugin.Register("version-grpc", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.GRPCPlugin, + ID: "version", Init: New, }) } diff --git a/snapshot/btrfs/btrfs.go b/snapshot/btrfs/btrfs.go index 422093f0a..deb85d7b6 100644 --- a/snapshot/btrfs/btrfs.go +++ b/snapshot/btrfs/btrfs.go @@ -20,11 +20,11 @@ import ( ) func init() { - plugin.Register("snapshot-btrfs", &plugin.Registration{ + plugin.Register(&plugin.Registration{ + ID: "btrfs", Type: plugin.SnapshotPlugin, Init: func(ic *plugin.InitContext) (interface{}, error) { - root := filepath.Join(ic.Root, "snapshot", "btrfs") - return NewSnapshotter(root) + return NewSnapshotter(ic.Root) }, }) } diff --git a/snapshot/naive/naive.go b/snapshot/naive/naive.go index 304409a03..88fc5539b 100644 --- a/snapshot/naive/naive.go +++ b/snapshot/naive/naive.go @@ -16,10 +16,11 @@ import ( ) func init() { - plugin.Register("snapshot-naive", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.SnapshotPlugin, + ID: "naive", Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "naive")) + return NewSnapshotter(ic.Root) }, }) } diff --git a/snapshot/overlay/overlay.go b/snapshot/overlay/overlay.go index 61fd1c1b8..a8775fa56 100644 --- a/snapshot/overlay/overlay.go +++ b/snapshot/overlay/overlay.go @@ -20,10 +20,11 @@ import ( ) func init() { - plugin.Register("snapshot-overlay", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.SnapshotPlugin, + ID: "overlayfs", Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "overlay")) + return NewSnapshotter(ic.Root) }, }) } diff --git a/snapshot/windows/windows.go b/snapshot/windows/windows.go index cb01907b8..0225ab3d3 100644 --- a/snapshot/windows/windows.go +++ b/snapshot/windows/windows.go @@ -4,7 +4,6 @@ package windows import ( "context" - "path/filepath" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/plugin" @@ -17,10 +16,11 @@ var ( ) func init() { - plugin.Register("snapshot-windows", &plugin.Registration{ + plugin.Register(&plugin.Registration{ Type: plugin.SnapshotPlugin, + ID: "windows", Init: func(ic *plugin.InitContext) (interface{}, error) { - return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "windows")) + return NewSnapshotter(ic.Root) }, }) } diff --git a/windows/runtime.go b/windows/runtime.go index 3c912f992..a126e5743 100644 --- a/windows/runtime.go +++ b/windows/runtime.go @@ -27,14 +27,15 @@ const ( var _ = (plugin.Runtime)(&Runtime{}) func init() { - plugin.Register(runtimeName, &plugin.Registration{ + plugin.Register(&plugin.Registration{ + ID: "windows", Type: plugin.RuntimePlugin, Init: New, }) } func New(ic *plugin.InitContext) (interface{}, error) { - rootDir := filepath.Join(ic.Root, runtimeName) + rootDir := filepath.Join(ic.Root) if err := os.MkdirAll(rootDir, 0755); err != nil { return nil, errors.Wrapf(err, "could not create state directory at %s", rootDir) } @@ -63,7 +64,7 @@ func New(ic *plugin.InitContext) (interface{}, error) { } // Try to delete the old state dir and recreate it - stateDir := filepath.Join(ic.State, runtimeName) + stateDir := filepath.Join(ic.Root, "state") if err := os.RemoveAll(stateDir); err != nil { log.G(c).WithError(err).Warnf("failed to cleanup old state directory at %s", stateDir) } @@ -99,6 +100,10 @@ type RuntimeSpec struct { hcs.Configuration } +func (r *Runtime) ID() string { + return fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtimeName) +} + func (r *Runtime) Create(ctx context.Context, id string, opts plugin.CreateOpts) (plugin.Task, error) { var rtSpec RuntimeSpec if err := json.Unmarshal(opts.Spec, &rtSpec); err != nil {