github.com/containerd/Containerd@v1.4.13/plugin/plugin.go (about) 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 17 package plugin 18 19 import ( 20 "fmt" 21 "sync" 22 23 "github.com/containerd/ttrpc" 24 "github.com/pkg/errors" 25 "google.golang.org/grpc" 26 ) 27 28 var ( 29 // ErrNoType is returned when no type is specified 30 ErrNoType = errors.New("plugin: no type") 31 // ErrNoPluginID is returned when no id is specified 32 ErrNoPluginID = errors.New("plugin: no id") 33 // ErrIDRegistered is returned when a duplicate id is already registered 34 ErrIDRegistered = errors.New("plugin: id already registered") 35 // ErrSkipPlugin is used when a plugin is not initialized and should not be loaded, 36 // this allows the plugin loader differentiate between a plugin which is configured 37 // not to load and one that fails to load. 38 ErrSkipPlugin = errors.New("skip plugin") 39 40 // ErrInvalidRequires will be thrown if the requirements for a plugin are 41 // defined in an invalid manner. 42 ErrInvalidRequires = errors.New("invalid requires") 43 ) 44 45 // IsSkipPlugin returns true if the error is skipping the plugin 46 func IsSkipPlugin(err error) bool { 47 return errors.Is(err, ErrSkipPlugin) 48 } 49 50 // Type is the type of the plugin 51 type Type string 52 53 func (t Type) String() string { return string(t) } 54 55 const ( 56 // InternalPlugin implements an internal plugin to containerd 57 InternalPlugin Type = "io.containerd.internal.v1" 58 // RuntimePlugin implements a runtime 59 RuntimePlugin Type = "io.containerd.runtime.v1" 60 // RuntimePluginV2 implements a runtime v2 61 RuntimePluginV2 Type = "io.containerd.runtime.v2" 62 // ServicePlugin implements a internal service 63 ServicePlugin Type = "io.containerd.service.v1" 64 // GRPCPlugin implements a grpc service 65 GRPCPlugin Type = "io.containerd.grpc.v1" 66 // SnapshotPlugin implements a snapshotter 67 SnapshotPlugin Type = "io.containerd.snapshotter.v1" 68 // TaskMonitorPlugin implements a task monitor 69 TaskMonitorPlugin Type = "io.containerd.monitor.v1" 70 // DiffPlugin implements a differ 71 DiffPlugin Type = "io.containerd.differ.v1" 72 // MetadataPlugin implements a metadata store 73 MetadataPlugin Type = "io.containerd.metadata.v1" 74 // ContentPlugin implements a content store 75 ContentPlugin Type = "io.containerd.content.v1" 76 // GCPlugin implements garbage collection policy 77 GCPlugin Type = "io.containerd.gc.v1" 78 ) 79 80 const ( 81 // RuntimeLinuxV1 is the legacy linux runtime 82 RuntimeLinuxV1 = "io.containerd.runtime.v1.linux" 83 // RuntimeRuncV1 is the runc runtime that supports a single container 84 RuntimeRuncV1 = "io.containerd.runc.v1" 85 // RuntimeRuncV2 is the runc runtime that supports multiple containers per shim 86 RuntimeRuncV2 = "io.containerd.runc.v2" 87 ) 88 89 // Registration contains information for registering a plugin 90 type Registration struct { 91 // Type of the plugin 92 Type Type 93 // ID of the plugin 94 ID string 95 // Config specific to the plugin 96 Config interface{} 97 // Requires is a list of plugins that the registered plugin requires to be available 98 Requires []Type 99 100 // InitFn is called when initializing a plugin. The registration and 101 // context are passed in. The init function may modify the registration to 102 // add exports, capabilities and platform support declarations. 103 InitFn func(*InitContext) (interface{}, error) 104 // Disable the plugin from loading 105 Disable bool 106 } 107 108 // Init the registered plugin 109 func (r *Registration) Init(ic *InitContext) *Plugin { 110 p, err := r.InitFn(ic) 111 return &Plugin{ 112 Registration: r, 113 Config: ic.Config, 114 Meta: ic.Meta, 115 instance: p, 116 err: err, 117 } 118 } 119 120 // URI returns the full plugin URI 121 func (r *Registration) URI() string { 122 return fmt.Sprintf("%s.%s", r.Type, r.ID) 123 } 124 125 // Service allows GRPC services to be registered with the underlying server 126 type Service interface { 127 Register(*grpc.Server) error 128 } 129 130 // TTRPCService allows TTRPC services to be registered with the underlying server 131 type TTRPCService interface { 132 RegisterTTRPC(*ttrpc.Server) error 133 } 134 135 // TCPService allows GRPC services to be registered with the underlying tcp server 136 type TCPService interface { 137 RegisterTCP(*grpc.Server) error 138 } 139 140 var register = struct { 141 sync.RWMutex 142 r []*Registration 143 }{} 144 145 // Load loads all plugins at the provided path into containerd 146 func Load(path string) (err error) { 147 defer func() { 148 if v := recover(); v != nil { 149 rerr, ok := v.(error) 150 if !ok { 151 rerr = fmt.Errorf("%s", v) 152 } 153 err = rerr 154 } 155 }() 156 return loadPlugins(path) 157 } 158 159 // Register allows plugins to register 160 func Register(r *Registration) { 161 register.Lock() 162 defer register.Unlock() 163 164 if r.Type == "" { 165 panic(ErrNoType) 166 } 167 if r.ID == "" { 168 panic(ErrNoPluginID) 169 } 170 if err := checkUnique(r); err != nil { 171 panic(err) 172 } 173 174 var last bool 175 for _, requires := range r.Requires { 176 if requires == "*" { 177 last = true 178 } 179 } 180 if last && len(r.Requires) != 1 { 181 panic(ErrInvalidRequires) 182 } 183 184 register.r = append(register.r, r) 185 } 186 187 func checkUnique(r *Registration) error { 188 for _, registered := range register.r { 189 if r.URI() == registered.URI() { 190 return errors.Wrap(ErrIDRegistered, r.URI()) 191 } 192 } 193 return nil 194 } 195 196 // DisableFilter filters out disabled plugins 197 type DisableFilter func(r *Registration) bool 198 199 // Graph returns an ordered list of registered plugins for initialization. 200 // Plugins in disableList specified by id will be disabled. 201 func Graph(filter DisableFilter) (ordered []*Registration) { 202 register.RLock() 203 defer register.RUnlock() 204 205 for _, r := range register.r { 206 if filter(r) { 207 r.Disable = true 208 } 209 } 210 211 added := map[*Registration]bool{} 212 for _, r := range register.r { 213 if r.Disable { 214 continue 215 } 216 children(r, added, &ordered) 217 if !added[r] { 218 ordered = append(ordered, r) 219 added[r] = true 220 } 221 } 222 return ordered 223 } 224 225 func children(reg *Registration, added map[*Registration]bool, ordered *[]*Registration) { 226 for _, t := range reg.Requires { 227 for _, r := range register.r { 228 if !r.Disable && 229 r.URI() != reg.URI() && 230 (t == "*" || r.Type == t) { 231 children(r, added, ordered) 232 if !added[r] { 233 *ordered = append(*ordered, r) 234 added[r] = true 235 } 236 } 237 } 238 } 239 }