Generalize the plugin package

Remove containerd specific parts of the plugin package to prepare its
move out of the main repository. Separate the plugin registration
singleton into a separate package.

Separating out the plugin package and registration makes it easier to
implement external plugins without creating a dependency loop.

Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
Derek McGowan
2023-10-12 15:36:02 -07:00
parent a80606bc2d
commit 7b2a918213
65 changed files with 363 additions and 299 deletions

View File

@@ -20,7 +20,6 @@ import (
"context"
"errors"
"fmt"
"sync"
)
var (
@@ -34,6 +33,10 @@ var (
// this allows the plugin loader differentiate between a plugin which is configured
// not to load and one that fails to load.
ErrSkipPlugin = errors.New("skip plugin")
// ErrPluginInitialized is used when a plugin is already initialized
ErrPluginInitialized = errors.New("plugin: already initialized")
// ErrPluginNotFound is used when a plugin is looked up but not found
ErrPluginNotFound = errors.New("plugin: not found")
// ErrInvalidRequires will be thrown if the requirements for a plugin are
// defined in an invalid manner.
@@ -65,8 +68,6 @@ type Registration struct {
// context are passed in. The init function may modify the registration to
// add exports, capabilities and platform support declarations.
InitFn func(*InitContext) (interface{}, error)
// Disable the plugin from loading
Disable bool
// ConfigMigration allows a plugin to migrate configurations from an older
// version to handle plugin renames or moving of features from one plugin
@@ -79,12 +80,12 @@ type Registration struct {
}
// Init the registered plugin
func (r *Registration) Init(ic *InitContext) *Plugin {
func (r Registration) Init(ic *InitContext) *Plugin {
p, err := r.InitFn(ic)
return &Plugin{
Registration: r,
Config: ic.Config,
Meta: ic.Meta,
Meta: *ic.Meta,
instance: p,
err: err,
}
@@ -95,11 +96,6 @@ func (r *Registration) URI() string {
return r.Type.String() + "." + r.ID
}
var register = struct {
sync.RWMutex
r []*Registration
}{}
// Load loads all plugins at the provided path into containerd.
//
// Load is currently only implemented on non-static, non-gccgo builds for amd64
@@ -118,18 +114,64 @@ func Load(path string) (err error) {
return loadPlugins(path)
}
// Register allows plugins to register
func Register(r *Registration) {
register.Lock()
defer register.Unlock()
// DisableFilter filters out disabled plugins
type DisableFilter func(r *Registration) bool
// Registry is list of registrations which can be registered to and
// produce a filtered and ordered output.
// The Registry itself is immutable and the list will be copied
// and appeneded to a new registry when new items are registered.
type Registry []*Registration
// Graph computes the ordered list of registrations based on their dependencies,
// filtering out any plugins which match the provided filter.
func (registry Registry) Graph(filter DisableFilter) []Registration {
disabled := map[*Registration]bool{}
for _, r := range registry {
if filter(r) {
disabled[r] = true
}
}
ordered := make([]Registration, 0, len(registry)-len(disabled))
added := map[*Registration]bool{}
for _, r := range registry {
if disabled[r] {
continue
}
children(r, registry, added, disabled, &ordered)
if !added[r] {
ordered = append(ordered, *r)
added[r] = true
}
}
return ordered
}
func children(reg *Registration, registry []*Registration, added, disabled map[*Registration]bool, ordered *[]Registration) {
for _, t := range reg.Requires {
for _, r := range registry {
if !disabled[r] && r.URI() != reg.URI() && (t == "*" || r.Type == t) {
children(r, registry, added, disabled, ordered)
if !added[r] {
*ordered = append(*ordered, *r)
added[r] = true
}
}
}
}
}
// Register adds the registration to a Registry and returns the
// updated Registry, panicking if registration could not succeed.
func (registry Registry) Register(r *Registration) Registry {
if r.Type == "" {
panic(ErrNoType)
}
if r.ID == "" {
panic(ErrNoPluginID)
}
if err := checkUnique(r); err != nil {
if err := checkUnique(registry, r); err != nil {
panic(err)
}
@@ -139,66 +181,14 @@ func Register(r *Registration) {
}
}
register.r = append(register.r, r)
return append(registry, r)
}
// Reset removes all global registrations
func Reset() {
register.Lock()
defer register.Unlock()
register.r = nil
}
func checkUnique(r *Registration) error {
for _, registered := range register.r {
func checkUnique(registry Registry, r *Registration) error {
for _, registered := range registry {
if r.URI() == registered.URI() {
return fmt.Errorf("%s: %w", r.URI(), ErrIDRegistered)
}
}
return nil
}
// DisableFilter filters out disabled plugins
type DisableFilter func(r *Registration) bool
// Graph returns an ordered list of registered plugins for initialization.
// Plugins in disableList specified by id will be disabled.
func Graph(filter DisableFilter) (ordered []*Registration) {
register.RLock()
defer register.RUnlock()
for _, r := range register.r {
if filter(r) {
r.Disable = true
}
}
added := map[*Registration]bool{}
for _, r := range register.r {
if r.Disable {
continue
}
children(r, added, &ordered)
if !added[r] {
ordered = append(ordered, r)
added[r] = true
}
}
return ordered
}
func children(reg *Registration, added map[*Registration]bool, ordered *[]*Registration) {
for _, t := range reg.Requires {
for _, r := range register.r {
if !r.Disable &&
r.URI() != reg.URI() &&
(t == "*" || r.Type == t) {
children(r, added, ordered)
if !added[r] {
*ordered = append(*ordered, r)
added[r] = true
}
}
}
}
}