1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package plugin
18
19import (
20	"context"
21	"path/filepath"
22
23	"github.com/containerd/containerd/errdefs"
24	"github.com/containerd/containerd/events/exchange"
25	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
26	"github.com/pkg/errors"
27)
28
29// InitContext is used for plugin inititalization
30type InitContext struct {
31	Context context.Context
32	Root    string
33	State   string
34	Config  interface{}
35	Address string
36	Events  *exchange.Exchange
37
38	Meta *Meta // plugins can fill in metadata at init.
39
40	plugins *Set
41}
42
43// NewContext returns a new plugin InitContext
44func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext {
45	return &InitContext{
46		Context: ctx,
47		Root:    filepath.Join(root, r.URI()),
48		State:   filepath.Join(state, r.URI()),
49		Meta: &Meta{
50			Exports: map[string]string{},
51		},
52		plugins: plugins,
53	}
54}
55
56// Get returns the first plugin by its type
57func (i *InitContext) Get(t Type) (interface{}, error) {
58	return i.plugins.Get(t)
59}
60
61// Meta contains information gathered from the registration and initialization
62// process.
63type Meta struct {
64	Platforms    []ocispec.Platform // platforms supported by plugin
65	Exports      map[string]string  // values exported by plugin
66	Capabilities []string           // feature switches for plugin
67}
68
69// Plugin represents an initialized plugin, used with an init context.
70type Plugin struct {
71	Registration *Registration // registration, as initialized
72	Config       interface{}   // config, as initialized
73	Meta         *Meta
74
75	instance interface{}
76	err      error // will be set if there was an error initializing the plugin
77}
78
79// Err returns the errors during initialization.
80// returns nil if not error was encountered
81func (p *Plugin) Err() error {
82	return p.err
83}
84
85// Instance returns the instance and any initialization error of the plugin
86func (p *Plugin) Instance() (interface{}, error) {
87	return p.instance, p.err
88}
89
90// Set defines a plugin collection, used with InitContext.
91//
92// This maintains ordering and unique indexing over the set.
93//
94// After iteratively instantiating plugins, this set should represent, the
95// ordered, initialization set of plugins for a containerd instance.
96type Set struct {
97	ordered     []*Plugin // order of initialization
98	byTypeAndID map[Type]map[string]*Plugin
99}
100
101// NewPluginSet returns an initialized plugin set
102func NewPluginSet() *Set {
103	return &Set{
104		byTypeAndID: make(map[Type]map[string]*Plugin),
105	}
106}
107
108// Add a plugin to the set
109func (ps *Set) Add(p *Plugin) error {
110	if byID, typeok := ps.byTypeAndID[p.Registration.Type]; !typeok {
111		ps.byTypeAndID[p.Registration.Type] = map[string]*Plugin{
112			p.Registration.ID: p,
113		}
114	} else if _, idok := byID[p.Registration.ID]; !idok {
115		byID[p.Registration.ID] = p
116	} else {
117		return errors.Wrapf(errdefs.ErrAlreadyExists, "plugin %v already initialized", p.Registration.URI())
118	}
119
120	ps.ordered = append(ps.ordered, p)
121	return nil
122}
123
124// Get returns the first plugin by its type
125func (ps *Set) Get(t Type) (interface{}, error) {
126	for _, v := range ps.byTypeAndID[t] {
127		return v.Instance()
128	}
129	return nil, errors.Wrapf(errdefs.ErrNotFound, "no plugins registered for %s", t)
130}
131
132// GetAll plugins in the set
133func (i *InitContext) GetAll() []*Plugin {
134	return i.plugins.ordered
135}
136
137// GetByType returns all plugins with the specific type.
138func (i *InitContext) GetByType(t Type) (map[string]*Plugin, error) {
139	p, ok := i.plugins.byTypeAndID[t]
140	if !ok {
141		return nil, errors.Wrapf(errdefs.ErrNotFound, "no plugins registered for %s", t)
142	}
143
144	return p, nil
145}
146