| /* | |
| Copyright The containerd Authors. | |
| Licensed under the Apache License, Version 2.0 (the "License"); | |
| you may not use this file except in compliance with the License. | |
| You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| See the License for the specific language governing permissions and | |
| limitations under the License. | |
| */ | |
| package plugin | |
| import ( | |
| "context" | |
| "fmt" | |
| imagespec "github.com/opencontainers/image-spec/specs-go/v1" | |
| ) | |
| // InitContext is used for plugin initialization | |
| type InitContext struct { | |
| Context context.Context | |
| Properties map[string]string | |
| Config interface{} | |
| RegisterReadiness func() func() | |
| // Meta is metadata plugins can fill in at init | |
| Meta *Meta | |
| plugins *Set | |
| } | |
| // NewContext returns a new plugin InitContext | |
| func NewContext(ctx context.Context, plugins *Set, properties map[string]string) *InitContext { | |
| if properties == nil { | |
| properties = map[string]string{} | |
| } | |
| return &InitContext{ | |
| Context: ctx, | |
| Properties: properties, | |
| Meta: &Meta{ | |
| Exports: map[string]string{}, | |
| }, | |
| plugins: plugins, | |
| } | |
| } | |
| // Meta contains information gathered from the registration and initialization | |
| // process. | |
| type Meta struct { | |
| Platforms []imagespec.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 | |
| } | |
| // Err returns the errors during initialization. | |
| // returns nil if no error was encountered | |
| func (p *Plugin) Err() error { | |
| return p.err | |
| } | |
| // Instance returns the instance and any initialization error of the plugin | |
| func (p *Plugin) Instance() (interface{}, error) { | |
| return p.instance, p.err | |
| } | |
| // Set 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 Set struct { | |
| ordered []*Plugin // order of initialization | |
| byTypeAndID map[Type]map[string]*Plugin | |
| } | |
| // NewPluginSet returns an initialized plugin set | |
| func NewPluginSet() *Set { | |
| return &Set{ | |
| byTypeAndID: make(map[Type]map[string]*Plugin), | |
| } | |
| } | |
| // Add a plugin to the set | |
| func (ps *Set) 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 fmt.Errorf("plugin add failed for %s: %w", p.Registration.URI(), ErrPluginInitialized) | |
| } | |
| ps.ordered = append(ps.ordered, p) | |
| return nil | |
| } | |
| // Get returns the plugin with the given type and id | |
| func (ps *Set) Get(t Type, id string) *Plugin { | |
| p, ok := ps.byTypeAndID[t] | |
| if !ok { | |
| return nil | |
| } | |
| return p[id] | |
| } | |
| // GetAll returns all initialized plugins | |
| func (ps *Set) GetAll() []*Plugin { | |
| return ps.ordered | |
| } | |
| // GetSingle returns a plugin instance of the given type when only a single instance | |
| // of that type is expected. Throws an ErrPluginNotFound if no plugin is found and | |
| // ErrPluginMultipleInstances when multiple instances are found. | |
| // Since plugins are not ordered, if multiple instances is suported then | |
| // GetByType should be used. If only one is expected, then to switch plugins, | |
| // disable or remove the unused plugins of the same type. | |
| func (i *InitContext) GetSingle(t Type) (interface{}, error) { | |
| var ( | |
| found bool | |
| instance interface{} | |
| ) | |
| for _, v := range i.plugins.byTypeAndID[t] { | |
| i, err := v.Instance() | |
| if err != nil { | |
| if IsSkipPlugin(err) { | |
| continue | |
| } | |
| return i, err | |
| } | |
| if found { | |
| return nil, fmt.Errorf("multiple plugins registered for %s: %w", t, ErrPluginMultipleInstances) | |
| } | |
| instance = i | |
| found = true | |
| } | |
| if !found { | |
| return nil, fmt.Errorf("no plugins registered for %s: %w", t, ErrPluginNotFound) | |
| } | |
| return instance, nil | |
| } | |
| // Plugins returns plugin set | |
| func (i *InitContext) Plugins() *Set { | |
| return i.plugins | |
| } | |
| // GetAll plugins in the set | |
| func (i *InitContext) GetAll() []*Plugin { | |
| return i.plugins.GetAll() | |
| } | |
| // GetByID returns the plugin of the given type and ID | |
| func (i *InitContext) GetByID(t Type, id string) (interface{}, error) { | |
| p := i.plugins.Get(t, id) | |
| if p == nil { | |
| return nil, fmt.Errorf("no plugins registered for %s.%s: %w", t, id, ErrPluginNotFound) | |
| } | |
| return p.Instance() | |
| } | |
| // GetByType returns all plugins with the specific type. | |
| func (i *InitContext) GetByType(t Type) (map[string]interface{}, error) { | |
| pi := map[string]interface{}{} | |
| for id, p := range i.plugins.byTypeAndID[t] { | |
| i, err := p.Instance() | |
| if err != nil { | |
| if IsSkipPlugin(err) { | |
| continue | |
| } | |
| return nil, err | |
| } | |
| pi[id] = i | |
| } | |
| if len(pi) == 0 { | |
| return nil, fmt.Errorf("no plugins registered for %s: %w", t, ErrPluginNotFound) | |
| } | |
| return pi, nil | |
| } |