plugin: refactor plugin system to support initialization reporting

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day
2017-10-09 20:43:04 -07:00
parent fe52d9369f
commit 8508e8252b
21 changed files with 216 additions and 80 deletions

View File

@@ -2,48 +2,123 @@ package plugin
import (
"context"
"fmt"
"path/filepath"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// NewContext returns a new plugin InitContext
func NewContext(ctx context.Context, plugins map[Type]map[string]interface{}, root, state, id string) *InitContext {
return &InitContext{
plugins: plugins,
Root: filepath.Join(root, id),
State: filepath.Join(state, id),
Context: log.WithModule(ctx, id),
}
}
// InitContext is used for plugin inititalization
type InitContext struct {
Context context.Context
Root string
State string
Address string
Context context.Context
Config interface{}
Address string
Events *events.Exchange
plugins map[Type]map[string]interface{}
Meta *Meta // plugins can fill in metadata at init.
plugins *PluginSet
}
// NewContext returns a new plugin InitContext
func NewContext(ctx context.Context, r *Registration, plugins *PluginSet, root, state string) *InitContext {
return &InitContext{
Context: log.WithModule(ctx, r.URI()),
Root: filepath.Join(root, r.URI()),
State: filepath.Join(state, r.URI()),
Meta: &Meta{
Exports: map[string]string{},
},
plugins: plugins,
}
}
// Get returns the first plugin by its type
func (i *InitContext) Get(t Type) (interface{}, error) {
for _, v := range i.plugins[t] {
return v, nil
}
return nil, fmt.Errorf("no plugins registered for %s", t)
return i.plugins.Get(t)
}
// GetAll returns all plugins with the specific type
func (i *InitContext) GetAll(t Type) (map[string]interface{}, error) {
p, ok := i.plugins[t]
if !ok {
return nil, fmt.Errorf("no plugins registered for %s", t)
// Meta contains information gathered from the registration and initialization
// process.
type Meta struct {
Platforms []ocispec.Platform // platforms supported by plugin
Exports map[string]string // values exported by plugin
Capabilities []string // feature switches for plugin
}
// Plugin represents an initialized plugin, used with an init context.
type Plugin struct {
Registration *Registration // registration, as initialized
Config interface{} // config, as initialized
Meta *Meta
instance interface{}
err error // will be set if there was an error initializing the plugin
}
func (p *Plugin) Err() error {
return p.err
}
func (p *Plugin) Instance() (interface{}, error) {
return p.instance, p.err
}
// PluginSet defines a plugin collection, used with InitContext.
//
// This maintains ordering and unique indexing over the set.
//
// After iteratively instantiating plugins, this set should represent, the
// ordered, initialization set of plugins for a containerd instance.
type PluginSet struct {
ordered []*Plugin // order of initialization
byTypeAndID map[Type]map[string]*Plugin
}
func NewPluginSet() *PluginSet {
return &PluginSet{
byTypeAndID: make(map[Type]map[string]*Plugin),
}
}
func (ps *PluginSet) Add(p *Plugin) error {
if byID, typeok := ps.byTypeAndID[p.Registration.Type]; !typeok {
ps.byTypeAndID[p.Registration.Type] = map[string]*Plugin{
p.Registration.ID: p,
}
} else if _, idok := byID[p.Registration.ID]; !idok {
byID[p.Registration.ID] = p
} else {
return errors.Wrapf(errdefs.ErrAlreadyExists, "plugin %v already initialized", p.Registration.URI())
}
ps.ordered = append(ps.ordered, p)
return nil
}
// Get returns the first plugin by its type
func (ps *PluginSet) Get(t Type) (interface{}, error) {
for _, v := range ps.byTypeAndID[t] {
return v.Instance()
}
return nil, errors.Wrapf(errdefs.ErrNotFound, "no plugins registered for %s", t)
}
func (i *InitContext) GetAll() []*Plugin {
return i.plugins.ordered
}
// GetByType returns all plugins with the specific type.
func (i *InitContext) GetByType(t Type) (map[string]*Plugin, error) {
p, ok := i.plugins.byTypeAndID[t]
if !ok {
return nil, errors.Wrapf(errdefs.ErrNotFound, "no plugins registered for %s", t)
}
return p, nil
}

View File

@@ -31,6 +31,8 @@ func IsSkipPlugin(err error) bool {
// Type is the type of the plugin
type Type string
func (t Type) String() string { return string(t) }
const (
// RuntimePlugin implements a runtime
RuntimePlugin Type = "io.containerd.runtime.v1"
@@ -54,9 +56,22 @@ type Registration struct {
ID string
Config interface{}
Requires []Type
Init func(*InitContext) (interface{}, error)
added bool
// InitFn is called when initializing a plugin. The registration and
// context are passed in. The init function may modify the registration to
// add exports, capabilites and platform support declarations.
InitFn func(*InitContext) (interface{}, error)
}
func (r *Registration) Init(ic *InitContext) *Plugin {
p, err := r.InitFn(ic)
return &Plugin{
Registration: r,
Config: ic.Config,
Meta: ic.Meta,
instance: p,
err: err,
}
}
// URI returns the full plugin URI