Setup plugin ids and dependencies

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2017-06-08 09:45:22 -07:00
parent 6bbed2c125
commit 94e7f8e943
31 changed files with 331 additions and 319 deletions

View File

@ -2,6 +2,7 @@ package containerd
import ( import (
"context" "context"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
@ -19,6 +20,7 @@ import (
versionservice "github.com/containerd/containerd/api/services/version" versionservice "github.com/containerd/containerd/api/services/version"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker"
contentservice "github.com/containerd/containerd/services/content" contentservice "github.com/containerd/containerd/services/content"
@ -83,7 +85,7 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
} }
return &Client{ return &Client{
conn: conn, conn: conn,
runtime: runtime.GOOS, runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
}, nil }, nil
} }

View File

@ -37,8 +37,6 @@ func loadConfig(path string) error {
// config specifies the containerd configuration file in the TOML format. // config specifies the containerd configuration file in the TOML format.
// It contains fields to configure various subsystems and containerd as a whole. // It contains fields to configure various subsystems and containerd as a whole.
type config struct { 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 is the path to a directory where containerd will store persistent data
Root string `toml:"root"` Root string `toml:"root"`
// GRPC configuration settings // GRPC configuration settings

View File

@ -2,8 +2,7 @@ package main
func defaultConfig() *config { func defaultConfig() *config {
return &config{ return &config{
Root: "/var/lib/containerd", Root: "/var/lib/containerd",
State: "/run/containerd",
GRPC: grpcConfig{ GRPC: grpcConfig{
Address: "/run/containerd/containerd.sock", Address: "/run/containerd/containerd.sock",
}, },
@ -11,7 +10,7 @@ func defaultConfig() *config {
Level: "info", Level: "info",
Address: "/run/containerd/debug.sock", Address: "/run/containerd/debug.sock",
}, },
Snapshotter: "overlay", Snapshotter: "io.containerd.snapshotter.v1.overlayfs",
Differ: "base", Differ: "io.containerd.differ.v1.base-diff",
} }
} }

View File

@ -4,8 +4,7 @@ package main
func defaultConfig() *config { func defaultConfig() *config {
return &config{ return &config{
Root: "/var/lib/containerd", Root: "/var/lib/containerd",
State: "/run/containerd",
GRPC: grpcConfig{ GRPC: grpcConfig{
Address: "/run/containerd/containerd.sock", Address: "/run/containerd/containerd.sock",
}, },
@ -13,7 +12,7 @@ func defaultConfig() *config {
Level: "info", Level: "info",
Address: "/run/containerd/debug.sock", Address: "/run/containerd/debug.sock",
}, },
Snapshotter: "naive", Snapshotter: "io.containerd.snapshotter.v1.naive",
Differ: "base", Differ: "io.containerd.differ.v1.base-diff",
} }
} }

View File

@ -7,8 +7,7 @@ import (
func defaultConfig() *config { func defaultConfig() *config {
return &config{ return &config{
Root: filepath.Join(os.Getenv("programfiles"), "containerd", "root"), Root: filepath.Join(os.Getenv("programfiles"), "containerd", "root"),
State: filepath.Join(os.Getenv("programfiles"), "containerd", "state"),
GRPC: grpcConfig{ GRPC: grpcConfig{
Address: `\\.\pipe\containerd-containerd`, Address: `\\.\pipe\containerd-containerd`,
}, },
@ -16,7 +15,7 @@ func defaultConfig() *config {
Level: "info", Level: "info",
Address: `\\.\pipe\containerd-debug`, Address: `\\.\pipe\containerd-debug`,
}, },
Snapshotter: "windows", Snapshotter: "io.containerd.snapshotter.v1.windows",
Differ: "base", Differ: "io.containerd.differ.v1.base-diff",
} }
} }

View File

@ -29,7 +29,6 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshot"
"github.com/containerd/containerd/sys" "github.com/containerd/containerd/sys"
"github.com/containerd/containerd/version" "github.com/containerd/containerd/version"
metrics "github.com/docker/go-metrics" metrics "github.com/docker/go-metrics"
@ -105,41 +104,42 @@ func main() {
if err := plugin.Load(filepath.Join(conf.Root, "plugins")); err != nil { if err := plugin.Load(filepath.Join(conf.Root, "plugins")); err != nil {
return err return err
} }
registerContentStore()
registerMetaDB()
// start debug and metrics APIs // start debug and metrics APIs
if err := serveDebugAPI(); err != nil { if err := serveDebugAPI(); err != nil {
return err return err
} }
monitor, err := loadMonitor()
if err != nil { var (
return err services []plugin.Service
} plugins = make(map[plugin.PluginType][]interface{})
runtimes, err := loadRuntimes(monitor) )
if err != nil { for _, init := range plugin.Graph() {
return err id := init.URI()
} log.G(global).WithField("type", init.Type).Infof("loading plugin %q...", id)
store, err := resolveContentStore() if !shouldLoad(init) {
if err != nil { continue
return err }
} ic := plugin.NewContext(plugins)
meta, err := resolveMetaDB(context) ic.Root = filepath.Join(conf.Root, id)
if err != nil { ic.Context = log.WithModule(global, id)
return err if init.Config != nil {
} if err := loadPluginConfig(init.ID, init.Config, ic); err != nil {
defer meta.Close() return err
snapshotter, err := loadSnapshotter(store) }
if 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 // start the GRPC api with the execution service registered
server := newGRPCServer() server := newGRPCServer()
for _, service := range services { for _, service := range services {
@ -159,7 +159,6 @@ func main() {
log.G(global).Infof("containerd successfully booted in %fs", time.Since(start).Seconds()) log.G(global).Infof("containerd successfully booted in %fs", time.Since(start).Seconds())
return handleSignals(signals, server) return handleSignals(signals, server)
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "containerd: %s\n", err) fmt.Fprintf(os.Stderr, "containerd: %s\n", err)
os.Exit(1) os.Exit(1)
@ -184,10 +183,6 @@ func before(context *cli.Context) error {
name: "root", name: "root",
d: &conf.Root, d: &conf.Root,
}, },
{
name: "state",
d: &conf.State,
},
{ {
name: "address", name: "address",
d: &conf.GRPC.Address, d: &conf.GRPC.Address,
@ -254,139 +249,27 @@ func serveDebugAPI() error {
return nil return nil
} }
func resolveContentStore() (content.Store, error) { func registerContentStore() {
cp := filepath.Join(conf.Root, "content") plugin.Register(&plugin.Registration{
return content.NewStore(cp) 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) { func registerMetaDB() {
path := filepath.Join(conf.Root, "meta.db") plugin.Register(&plugin.Registration{
Type: plugin.MetadataPlugin,
db, err := bolt.Open(path, 0644, nil) ID: "bolt",
if err != nil { Init: func(ic *plugin.InitContext) (interface{}, error) {
return nil, err if err := os.MkdirAll(ic.Root, 0700); err != nil {
}
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 {
return nil, err return nil, err
} }
ic.Config = rr.Config return bolt.Open(filepath.Join(ic.Root, "meta.db"), 0644, nil)
} },
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)
} }
func newGRPCServer() *grpc.Server { func newGRPCServer() *grpc.Server {
@ -397,40 +280,6 @@ func newGRPCServer() *grpc.Server {
return s 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 { func serveGRPC(server *grpc.Server) error {
path := conf.GRPC.Address path := conf.GRPC.Address
if path == "" { if path == "" {
@ -494,3 +343,23 @@ func dumpStacks() {
buf = buf[:stackSize] buf = buf[:stackSize]
logrus.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 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
}
}

View File

@ -12,13 +12,14 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
const ( const defaultConfigPath = "/etc/containerd/config.toml"
defaultConfigPath = "/etc/containerd/config.toml"
)
var ( var handledSignals = []os.Signal{
handledSignals = []os.Signal{unix.SIGTERM, unix.SIGINT, unix.SIGUSR1, unix.SIGCHLD} unix.SIGTERM,
) unix.SIGINT,
unix.SIGUSR1,
unix.SIGCHLD,
}
func platformInit(context *cli.Context) error { func platformInit(context *cli.Context) error {
if conf.Subreaper { if conf.Subreaper {
@ -33,12 +34,6 @@ func platformInit(context *cli.Context) error {
return err 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 return nil
} }

View File

@ -22,12 +22,6 @@ var (
) )
func platformInit(context *cli.Context) error { 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 return nil
} }

View File

@ -55,8 +55,8 @@ var runCommand = cli.Command{
}, },
cli.StringFlag{ cli.StringFlag{
Name: "runtime", Name: "runtime",
Usage: "runtime name (linux, windows, vmware-linux)", Usage: "runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux)",
Value: "linux", Value: "io.containerd.runtime.v1.linux",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "readonly", Name: "readonly",

View File

@ -18,10 +18,23 @@ import (
) )
func init() { func init() {
plugin.Register("diff-base", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.DiffPlugin, Type: plugin.DiffPlugin,
ID: "base-diff",
Requires: []plugin.PluginType{
plugin.ContentPlugin,
plugin.SnapshotPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { 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))
}, },
}) })
} }

View File

@ -32,19 +32,23 @@ import (
var ( var (
ErrTaskNotExists = errors.New("task does not exist") ErrTaskNotExists = errors.New("task does not exist")
ErrTaskAlreadyExists = errors.New("task already exists") ErrTaskAlreadyExists = errors.New("task already exists")
pluginID = fmt.Sprintf("%s.%s", plugin.RuntimePlugin, "linux")
) )
const ( const (
runtimeName = "linux"
configFilename = "config.json" configFilename = "config.json"
defaultRuntime = "runc" defaultRuntime = "runc"
defaultShim = "containerd-shim" defaultShim = "containerd-shim"
) )
func init() { func init() {
plugin.Register(runtimeName, &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.RuntimePlugin, Type: plugin.RuntimePlugin,
ID: "linux",
Init: New, Init: New,
Requires: []plugin.PluginType{
plugin.TaskMonitorPlugin,
},
Config: &Config{ Config: &Config{
Shim: defaultShim, Shim: defaultShim,
Runtime: defaultRuntime, Runtime: defaultRuntime,
@ -129,25 +133,28 @@ func (l *taskList) delete(ctx context.Context, t *Task) {
} }
func New(ic *plugin.InitContext) (interface{}, error) { func New(ic *plugin.InitContext) (interface{}, error) {
path := filepath.Join(ic.State, runtimeName) if err := os.MkdirAll(ic.Root, 0700); err != nil {
if err := os.MkdirAll(path, 0700); err != nil { return nil, err
}
monitor, err := ic.Get(plugin.TaskMonitorPlugin)
if err != nil {
return nil, err return nil, err
} }
cfg := ic.Config.(*Config) cfg := ic.Config.(*Config)
c, cancel := context.WithCancel(ic.Context) c, cancel := context.WithCancel(ic.Context)
r := &Runtime{ r := &Runtime{
root: path, root: ic.Root,
remote: !cfg.NoShim, remote: !cfg.NoShim,
shim: cfg.Shim, shim: cfg.Shim,
runtime: cfg.Runtime, runtime: cfg.Runtime,
events: make(chan *plugin.Event, 2048), events: make(chan *plugin.Event, 2048),
eventsContext: c, eventsContext: c,
eventsCancel: cancel, eventsCancel: cancel,
monitor: ic.Monitor, monitor: monitor.(plugin.TaskMonitor),
tasks: newTaskList(), tasks: newTaskList(),
} }
// set the events output for a monitor if it generates events // 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) tasks, err := r.loadAllTasks(ic.Context)
if err != nil { if err != nil {
return nil, err return nil, err
@ -157,7 +164,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
return nil, err return nil, err
} }
} }
// load all tasks from disk
return r, nil return r, nil
} }
@ -174,6 +180,10 @@ type Runtime struct {
tasks *taskList 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) { func (r *Runtime) Create(ctx context.Context, id string, opts plugin.CreateOpts) (t plugin.Task, err error) {
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
@ -358,7 +368,7 @@ func (r *Runtime) forward(events shim.Shim_EventsClient) {
} }
r.events <- &plugin.Event{ r.events <- &plugin.Event{
Timestamp: time.Now(), Timestamp: time.Now(),
Runtime: runtimeName, Runtime: r.ID(),
Type: et, Type: et,
Pid: e.Pid, Pid: e.Pid,
ID: e.ID, ID: e.ID,

View File

@ -35,7 +35,7 @@ func (c *Task) Info() plugin.TaskInfo {
return plugin.TaskInfo{ return plugin.TaskInfo{
ID: c.containerID, ID: c.containerID,
ContainerID: c.containerID, ContainerID: c.containerID,
Runtime: runtimeName, Runtime: pluginID,
Spec: c.spec, Spec: c.spec,
Namespace: c.namespace, Namespace: c.namespace,
} }

View File

@ -13,11 +13,10 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
const name = "cgroups"
func init() { func init() {
plugin.Register(name, &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.TaskMonitorPlugin, Type: plugin.TaskMonitorPlugin,
ID: "cgroups",
Init: New, Init: New,
}) })
} }

36
plugin/context.go Normal file
View File

@ -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
}

View File

@ -3,7 +3,6 @@ package plugin
import "errors" import "errors"
var ( var (
ErrUnknownRuntime = errors.New("unknown runtime")
ErrContainerExists = errors.New("container with id already exists") ErrContainerExists = errors.New("container with id already exists")
ErrContainerNotExist = errors.New("container does not exist") ErrContainerNotExist = errors.New("container does not exist")
ErrRuntimeNotExist = errors.New("runtime does not exist") ErrRuntimeNotExist = errors.New("runtime does not exist")

View File

@ -1,45 +1,42 @@
package plugin package plugin
import ( import (
"errors"
"fmt" "fmt"
"sync" "sync"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/snapshot"
"golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type PluginType int var (
ErrNoPluginType = errors.New("plugin: no type")
ErrNoPluginID = errors.New("plugin: no id")
)
type PluginType string
const ( const (
RuntimePlugin PluginType = iota + 1 RuntimePlugin PluginType = "io.containerd.runtime.v1"
GRPCPlugin GRPCPlugin PluginType = "io.containerd.grpc.v1"
SnapshotPlugin SnapshotPlugin PluginType = "io.containerd.snapshotter.v1"
TaskMonitorPlugin TaskMonitorPlugin PluginType = "io.containerd.monitor.v1"
DiffPlugin DiffPlugin PluginType = "io.containerd.differ.v1"
MetadataPlugin PluginType = "io.containerd.metadata.v1"
ContentPlugin PluginType = "io.containerd.content.v1"
) )
type Registration struct { type Registration struct {
Type PluginType Type PluginType
Config interface{} ID string
Init func(*InitContext) (interface{}, error) 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? func (r *Registration) URI() string {
type InitContext struct { return fmt.Sprintf("%s.%s", r.Type, r.ID)
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
} }
type Service interface { type Service interface {
@ -48,10 +45,8 @@ type Service interface {
var register = struct { var register = struct {
sync.Mutex sync.Mutex
r map[string]*Registration r []*Registration
}{ }{}
r: make(map[string]*Registration),
}
// Load loads all plugins at the provided path into containerd // Load loads all plugins at the provided path into containerd
func Load(path string) (err error) { func Load(path string) (err error) {
@ -67,16 +62,39 @@ func Load(path string) (err error) {
return loadPlugins(path) return loadPlugins(path)
} }
func Register(name string, r *Registration) error { func Register(r *Registration) {
register.Lock() register.Lock()
defer register.Unlock() defer register.Unlock()
if _, ok := register.r[name]; ok { if r.Type == "" {
return fmt.Errorf("plugin already registered as %q", name) panic(ErrNoPluginType)
} }
register.r[name] = r if r.ID == "" {
return nil panic(ErrNoPluginID)
}
register.r = append(register.r, r)
} }
func Registrations() map[string]*Registration { func Graph() (ordered []*Registration) {
return register.r 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
}
}
}
}
} }

View File

@ -32,6 +32,8 @@ type Exit struct {
// Runtime is responsible for the creation of containers for a certain platform, // Runtime is responsible for the creation of containers for a certain platform,
// arch, or custom usage. // arch, or custom usage.
type Runtime interface { type Runtime interface {
// ID of the runtime
ID() string
// Create creates a container with the provided id and options // Create creates a container with the provided id and options
Create(ctx context.Context, id string, opts CreateOpts) (Task, error) Create(ctx context.Context, id string, opts CreateOpts) (Task, error)
// Get returns a container // Get returns a container

View File

@ -13,10 +13,18 @@ import (
) )
func init() { func init() {
plugin.Register("containers-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "containers",
Requires: []plugin.PluginType{
plugin.MetadataPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { 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
}, },
}) })
} }

View File

@ -30,15 +30,23 @@ var bufPool = sync.Pool{
var _ api.ContentServer = &Service{} var _ api.ContentServer = &Service{}
func init() { func init() {
plugin.Register("content-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "content",
Requires: []plugin.PluginType{
plugin.ContentPlugin,
},
Init: NewService, Init: NewService,
}) })
} }
func NewService(ic *plugin.InitContext) (interface{}, error) { func NewService(ic *plugin.InitContext) (interface{}, error) {
c, err := ic.Get(plugin.ContentPlugin)
if err != nil {
return nil, err
}
return &Service{ return &Service{
store: ic.Content, store: c.(content.Store),
}, nil }, nil
} }

View File

@ -10,11 +10,19 @@ import (
) )
func init() { func init() {
plugin.Register("diff-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "diff",
Requires: []plugin.PluginType{
plugin.DiffPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { Init: func(ic *plugin.InitContext) (interface{}, error) {
d, err := ic.Get(plugin.DiffPlugin)
if err != nil {
return nil, err
}
return &service{ return &service{
diff: ic.Differ, diff: d.(plugin.Differ),
}, nil }, nil
}, },
}) })

View File

@ -35,22 +35,45 @@ var (
) )
func init() { func init() {
plugin.Register("tasks-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "tasks",
Requires: []plugin.PluginType{
plugin.RuntimePlugin,
plugin.MetadataPlugin,
plugin.ContentPlugin,
},
Init: New, Init: New,
}) })
} }
func New(ic *plugin.InitContext) (interface{}, error) { 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 { if err != nil {
return nil, err return nil, err
} }
return &Service{ return &Service{
runtimes: ic.Runtimes, runtimes: runtimes,
db: ic.Meta, db: m.(*bolt.DB),
collector: c, collector: c,
store: ic.Content, store: ct.(content.Store),
}, nil }, 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) { func (s *Service) getRuntime(name string) (plugin.Runtime, error) {
runtime, ok := s.runtimes[name] runtime, ok := s.runtimes[name]
if !ok { if !ok {
return nil, plugin.ErrUnknownRuntime return nil, fmt.Errorf("unknown runtime %q", name)
} }
return runtime, nil return runtime, nil
} }

View File

@ -13,8 +13,9 @@ type Service struct {
} }
func init() { func init() {
plugin.Register("healthcheck-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "healthcheck",
Init: NewService, Init: NewService,
}) })
} }

View File

@ -12,10 +12,18 @@ import (
) )
func init() { func init() {
plugin.Register("images-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "images",
Requires: []plugin.PluginType{
plugin.MetadataPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { 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
}, },
}) })
} }

View File

@ -15,10 +15,18 @@ import (
) )
func init() { func init() {
plugin.Register("namespaces-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "namespaces",
Requires: []plugin.PluginType{
plugin.MetadataPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { 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
}, },
}) })
} }

View File

@ -16,10 +16,18 @@ import (
) )
func init() { func init() {
plugin.Register("snapshots-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "snapshots",
Requires: []plugin.PluginType{
plugin.SnapshotPlugin,
},
Init: func(ic *plugin.InitContext) (interface{}, error) { 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))
}, },
}) })
} }

View File

@ -12,8 +12,9 @@ import (
var _ api.VersionServer = &Service{} var _ api.VersionServer = &Service{}
func init() { func init() {
plugin.Register("version-grpc", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.GRPCPlugin, Type: plugin.GRPCPlugin,
ID: "version",
Init: New, Init: New,
}) })
} }

View File

@ -20,11 +20,11 @@ import (
) )
func init() { func init() {
plugin.Register("snapshot-btrfs", &plugin.Registration{ plugin.Register(&plugin.Registration{
ID: "btrfs",
Type: plugin.SnapshotPlugin, Type: plugin.SnapshotPlugin,
Init: func(ic *plugin.InitContext) (interface{}, error) { Init: func(ic *plugin.InitContext) (interface{}, error) {
root := filepath.Join(ic.Root, "snapshot", "btrfs") return NewSnapshotter(ic.Root)
return NewSnapshotter(root)
}, },
}) })
} }

View File

@ -16,10 +16,11 @@ import (
) )
func init() { func init() {
plugin.Register("snapshot-naive", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.SnapshotPlugin, Type: plugin.SnapshotPlugin,
ID: "naive",
Init: func(ic *plugin.InitContext) (interface{}, error) { Init: func(ic *plugin.InitContext) (interface{}, error) {
return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "naive")) return NewSnapshotter(ic.Root)
}, },
}) })
} }

View File

@ -20,10 +20,11 @@ import (
) )
func init() { func init() {
plugin.Register("snapshot-overlay", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.SnapshotPlugin, Type: plugin.SnapshotPlugin,
ID: "overlayfs",
Init: func(ic *plugin.InitContext) (interface{}, error) { Init: func(ic *plugin.InitContext) (interface{}, error) {
return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "overlay")) return NewSnapshotter(ic.Root)
}, },
}) })
} }

View File

@ -4,7 +4,6 @@ package windows
import ( import (
"context" "context"
"path/filepath"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
@ -17,10 +16,11 @@ var (
) )
func init() { func init() {
plugin.Register("snapshot-windows", &plugin.Registration{ plugin.Register(&plugin.Registration{
Type: plugin.SnapshotPlugin, Type: plugin.SnapshotPlugin,
ID: "windows",
Init: func(ic *plugin.InitContext) (interface{}, error) { Init: func(ic *plugin.InitContext) (interface{}, error) {
return NewSnapshotter(filepath.Join(ic.Root, "snapshot", "windows")) return NewSnapshotter(ic.Root)
}, },
}) })
} }

View File

@ -27,14 +27,15 @@ const (
var _ = (plugin.Runtime)(&Runtime{}) var _ = (plugin.Runtime)(&Runtime{})
func init() { func init() {
plugin.Register(runtimeName, &plugin.Registration{ plugin.Register(&plugin.Registration{
ID: "windows",
Type: plugin.RuntimePlugin, Type: plugin.RuntimePlugin,
Init: New, Init: New,
}) })
} }
func New(ic *plugin.InitContext) (interface{}, error) { 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 { if err := os.MkdirAll(rootDir, 0755); err != nil {
return nil, errors.Wrapf(err, "could not create state directory at %s", rootDir) 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 // 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 { if err := os.RemoveAll(stateDir); err != nil {
log.G(c).WithError(err).Warnf("failed to cleanup old state directory at %s", stateDir) log.G(c).WithError(err).Warnf("failed to cleanup old state directory at %s", stateDir)
} }
@ -99,6 +100,10 @@ type RuntimeSpec struct {
hcs.Configuration 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) { func (r *Runtime) Create(ctx context.Context, id string, opts plugin.CreateOpts) (plugin.Task, error) {
var rtSpec RuntimeSpec var rtSpec RuntimeSpec
if err := json.Unmarshal(opts.Spec, &rtSpec); err != nil { if err := json.Unmarshal(opts.Spec, &rtSpec); err != nil {