Skip to content

Commit 602343a

Browse files
committed
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>
1 parent 7163bfa commit 602343a

4 files changed

Lines changed: 177 additions & 174 deletions

File tree

context.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,15 @@ package plugin
1919
import (
2020
"context"
2121
"fmt"
22-
"path/filepath"
2322

24-
"github.com/containerd/containerd/errdefs"
25-
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
23+
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
2624
)
2725

2826
// InitContext is used for plugin initialization
2927
type InitContext struct {
3028
Context context.Context
31-
Root string
32-
State string
29+
Properties map[string]string
3330
Config interface{}
34-
Address string
35-
TTRPCAddress string
3631
RegisterReadiness func() func()
3732

3833
// Meta is metadata plugins can fill in at init
@@ -42,11 +37,13 @@ type InitContext struct {
4237
}
4338

4439
// NewContext returns a new plugin InitContext
45-
func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext {
40+
func NewContext(ctx context.Context, plugins *Set, properties map[string]string) *InitContext {
41+
if properties == nil {
42+
properties = map[string]string{}
43+
}
4644
return &InitContext{
47-
Context: ctx,
48-
Root: filepath.Join(root, r.URI()),
49-
State: filepath.Join(state, r.URI()),
45+
Context: ctx,
46+
Properties: properties,
5047
Meta: &Meta{
5148
Exports: map[string]string{},
5249
},
@@ -62,16 +59,16 @@ func (i *InitContext) Get(t Type) (interface{}, error) {
6259
// Meta contains information gathered from the registration and initialization
6360
// process.
6461
type Meta struct {
65-
Platforms []ocispec.Platform // platforms supported by plugin
66-
Exports map[string]string // values exported by plugin
67-
Capabilities []string // feature switches for plugin
62+
Platforms []imagespec.Platform // platforms supported by plugin
63+
Exports map[string]string // values exported by plugin
64+
Capabilities []string // feature switches for plugin
6865
}
6966

7067
// Plugin represents an initialized plugin, used with an init context.
7168
type Plugin struct {
72-
Registration *Registration // registration, as initialized
73-
Config interface{} // config, as initialized
74-
Meta *Meta
69+
Registration Registration // registration, as initialized
70+
Config interface{} // config, as initialized
71+
Meta Meta
7572

7673
instance interface{}
7774
err error // will be set if there was an error initializing the plugin
@@ -115,7 +112,7 @@ func (ps *Set) Add(p *Plugin) error {
115112
} else if _, idok := byID[p.Registration.ID]; !idok {
116113
byID[p.Registration.ID] = p
117114
} else {
118-
return fmt.Errorf("plugin %v already initialized: %w", p.Registration.URI(), errdefs.ErrAlreadyExists)
115+
return fmt.Errorf("plugin add failed for %s: %w", p.Registration.URI(), ErrPluginInitialized)
119116
}
120117

121118
ps.ordered = append(ps.ordered, p)
@@ -127,7 +124,7 @@ func (ps *Set) Get(t Type) (interface{}, error) {
127124
for _, v := range ps.byTypeAndID[t] {
128125
return v.Instance()
129126
}
130-
return nil, fmt.Errorf("no plugins registered for %s: %w", t, errdefs.ErrNotFound)
127+
return nil, fmt.Errorf("no plugins registered for %s: %w", t, ErrPluginNotFound)
131128
}
132129

133130
// GetAll returns all initialized plugins
@@ -153,7 +150,7 @@ func (i *InitContext) GetByID(t Type, id string) (interface{}, error) {
153150
}
154151
p, ok := ps[id]
155152
if !ok {
156-
return nil, fmt.Errorf("no %s plugins with id %s: %w", t, id, errdefs.ErrNotFound)
153+
return nil, fmt.Errorf("no %s plugins with id %s: %w", t, id, ErrPluginNotFound)
157154
}
158155
return p.Instance()
159156
}
@@ -162,7 +159,7 @@ func (i *InitContext) GetByID(t Type, id string) (interface{}, error) {
162159
func (i *InitContext) GetByType(t Type) (map[string]*Plugin, error) {
163160
p, ok := i.plugins.byTypeAndID[t]
164161
if !ok {
165-
return nil, fmt.Errorf("no plugins registered for %s: %w", t, errdefs.ErrNotFound)
162+
return nil, fmt.Errorf("no plugins registered for %s: %w", t, ErrPluginNotFound)
166163
}
167164

168165
return p, nil

plugin.go

Lines changed: 59 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23-
"sync"
2423
)
2524

2625
var (
@@ -34,6 +33,10 @@ var (
3433
// this allows the plugin loader differentiate between a plugin which is configured
3534
// not to load and one that fails to load.
3635
ErrSkipPlugin = errors.New("skip plugin")
36+
// ErrPluginInitialized is used when a plugin is already initialized
37+
ErrPluginInitialized = errors.New("plugin: already initialized")
38+
// ErrPluginNotFound is used when a plugin is looked up but not found
39+
ErrPluginNotFound = errors.New("plugin: not found")
3740

3841
// ErrInvalidRequires will be thrown if the requirements for a plugin are
3942
// defined in an invalid manner.
@@ -65,8 +68,6 @@ type Registration struct {
6568
// context are passed in. The init function may modify the registration to
6669
// add exports, capabilities and platform support declarations.
6770
InitFn func(*InitContext) (interface{}, error)
68-
// Disable the plugin from loading
69-
Disable bool
7071

7172
// ConfigMigration allows a plugin to migrate configurations from an older
7273
// version to handle plugin renames or moving of features from one plugin
@@ -79,12 +80,12 @@ type Registration struct {
7980
}
8081

8182
// Init the registered plugin
82-
func (r *Registration) Init(ic *InitContext) *Plugin {
83+
func (r Registration) Init(ic *InitContext) *Plugin {
8384
p, err := r.InitFn(ic)
8485
return &Plugin{
8586
Registration: r,
8687
Config: ic.Config,
87-
Meta: ic.Meta,
88+
Meta: *ic.Meta,
8889
instance: p,
8990
err: err,
9091
}
@@ -95,11 +96,6 @@ func (r *Registration) URI() string {
9596
return r.Type.String() + "." + r.ID
9697
}
9798

98-
var register = struct {
99-
sync.RWMutex
100-
r []*Registration
101-
}{}
102-
10399
// Load loads all plugins at the provided path into containerd.
104100
//
105101
// Load is currently only implemented on non-static, non-gccgo builds for amd64
@@ -118,87 +114,81 @@ func Load(path string) (err error) {
118114
return loadPlugins(path)
119115
}
120116

121-
// Register allows plugins to register
122-
func Register(r *Registration) {
123-
register.Lock()
124-
defer register.Unlock()
125-
126-
if r.Type == "" {
127-
panic(ErrNoType)
128-
}
129-
if r.ID == "" {
130-
panic(ErrNoPluginID)
131-
}
132-
if err := checkUnique(r); err != nil {
133-
panic(err)
134-
}
135-
136-
for _, requires := range r.Requires {
137-
if requires == "*" && len(r.Requires) != 1 {
138-
panic(ErrInvalidRequires)
139-
}
140-
}
141-
142-
register.r = append(register.r, r)
143-
}
144-
145-
// Reset removes all global registrations
146-
func Reset() {
147-
register.Lock()
148-
defer register.Unlock()
149-
register.r = nil
150-
}
151-
152-
func checkUnique(r *Registration) error {
153-
for _, registered := range register.r {
154-
if r.URI() == registered.URI() {
155-
return fmt.Errorf("%s: %w", r.URI(), ErrIDRegistered)
156-
}
157-
}
158-
return nil
159-
}
160-
161117
// DisableFilter filters out disabled plugins
162118
type DisableFilter func(r *Registration) bool
163119

164-
// Graph returns an ordered list of registered plugins for initialization.
165-
// Plugins in disableList specified by id will be disabled.
166-
func Graph(filter DisableFilter) (ordered []*Registration) {
167-
register.RLock()
168-
defer register.RUnlock()
169-
170-
for _, r := range register.r {
120+
// Registry is list of registrations which can be registered to and
121+
// produce a filtered and ordered output.
122+
// The Registry itself is immutable and the list will be copied
123+
// and appeneded to a new registry when new items are registered.
124+
type Registry []*Registration
125+
126+
// Graph computes the ordered list of registrations based on their dependencies,
127+
// filtering out any plugins which match the provided filter.
128+
func (registry Registry) Graph(filter DisableFilter) []Registration {
129+
disabled := map[*Registration]bool{}
130+
for _, r := range registry {
171131
if filter(r) {
172-
r.Disable = true
132+
disabled[r] = true
173133
}
174134
}
175135

136+
ordered := make([]Registration, 0, len(registry)-len(disabled))
176137
added := map[*Registration]bool{}
177-
for _, r := range register.r {
178-
if r.Disable {
138+
for _, r := range registry {
139+
if disabled[r] {
179140
continue
180141
}
181-
children(r, added, &ordered)
142+
children(r, registry, added, disabled, &ordered)
182143
if !added[r] {
183-
ordered = append(ordered, r)
144+
ordered = append(ordered, *r)
184145
added[r] = true
185146
}
186147
}
187148
return ordered
188149
}
189150

190-
func children(reg *Registration, added map[*Registration]bool, ordered *[]*Registration) {
151+
func children(reg *Registration, registry []*Registration, added, disabled map[*Registration]bool, ordered *[]Registration) {
191152
for _, t := range reg.Requires {
192-
for _, r := range register.r {
193-
if !r.Disable &&
194-
r.URI() != reg.URI() &&
195-
(t == "*" || r.Type == t) {
196-
children(r, added, ordered)
153+
for _, r := range registry {
154+
if !disabled[r] && r.URI() != reg.URI() && (t == "*" || r.Type == t) {
155+
children(r, registry, added, disabled, ordered)
197156
if !added[r] {
198-
*ordered = append(*ordered, r)
157+
*ordered = append(*ordered, *r)
199158
added[r] = true
200159
}
201160
}
202161
}
203162
}
204163
}
164+
165+
// Register adds the registration to a Registry and returns the
166+
// updated Registry, panicking if registration could not succeed.
167+
func (registry Registry) Register(r *Registration) Registry {
168+
if r.Type == "" {
169+
panic(ErrNoType)
170+
}
171+
if r.ID == "" {
172+
panic(ErrNoPluginID)
173+
}
174+
if err := checkUnique(registry, r); err != nil {
175+
panic(err)
176+
}
177+
178+
for _, requires := range r.Requires {
179+
if requires == "*" && len(r.Requires) != 1 {
180+
panic(ErrInvalidRequires)
181+
}
182+
}
183+
184+
return append(registry, r)
185+
}
186+
187+
func checkUnique(registry Registry, r *Registration) error {
188+
for _, registered := range registry {
189+
if r.URI() == registered.URI() {
190+
return fmt.Errorf("%s: %w", r.URI(), ErrIDRegistered)
191+
}
192+
}
193+
return nil
194+
}

0 commit comments

Comments
 (0)