plugin.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package plugin
  14. import (
  15. "fmt"
  16. "sync"
  17. "github.com/pkg/errors"
  18. "google.golang.org/grpc"
  19. )
  20. var (
  21. // ErrNoType is returned when no type is specified
  22. ErrNoType = errors.New("plugin: no type")
  23. // ErrNoPluginID is returned when no id is specified
  24. ErrNoPluginID = errors.New("plugin: no id")
  25. // ErrSkipPlugin is used when a plugin is not initialized and should not be loaded,
  26. // this allows the plugin loader differentiate between a plugin which is configured
  27. // not to load and one that fails to load.
  28. ErrSkipPlugin = errors.New("skip plugin")
  29. // ErrInvalidRequires will be thrown if the requirements for a plugin are
  30. // defined in an invalid manner.
  31. ErrInvalidRequires = errors.New("invalid requires")
  32. )
  33. // IsSkipPlugin returns true if the error is skipping the plugin
  34. func IsSkipPlugin(err error) bool {
  35. return errors.Cause(err) == ErrSkipPlugin
  36. }
  37. // Type is the type of the plugin
  38. type Type string
  39. func (t Type) String() string { return string(t) }
  40. const (
  41. // InternalPlugin implements an internal plugin to containerd
  42. InternalPlugin Type = "io.containerd.internal.v1"
  43. // RuntimePlugin implements a runtime
  44. RuntimePlugin Type = "io.containerd.runtime.v1"
  45. // RuntimePluginV2 implements a runtime v2
  46. RuntimePluginV2 Type = "io.containerd.runtime.v2"
  47. // ServicePlugin implements a internal service
  48. ServicePlugin Type = "io.containerd.service.v1"
  49. // GRPCPlugin implements a grpc service
  50. GRPCPlugin Type = "io.containerd.grpc.v1"
  51. // SnapshotPlugin implements a snapshotter
  52. SnapshotPlugin Type = "io.containerd.snapshotter.v1"
  53. // TaskMonitorPlugin implements a task monitor
  54. TaskMonitorPlugin Type = "io.containerd.monitor.v1"
  55. // DiffPlugin implements a differ
  56. DiffPlugin Type = "io.containerd.differ.v1"
  57. // MetadataPlugin implements a metadata store
  58. MetadataPlugin Type = "io.containerd.metadata.v1"
  59. // ContentPlugin implements a content store
  60. ContentPlugin Type = "io.containerd.content.v1"
  61. // GCPlugin implements garbage collection policy
  62. GCPlugin Type = "io.containerd.gc.v1"
  63. )
  64. const (
  65. // RuntimeLinuxV1 is the legacy linux runtime
  66. RuntimeLinuxV1 = "io.containerd.runtime.v1.linux"
  67. // RuntimeRuncV1 is the runc runtime that supports a single container
  68. RuntimeRuncV1 = "io.containerd.runc.v1"
  69. // RuntimeRuncV2 is the runc runtime that supports multiple containers per shim
  70. RuntimeRuncV2 = "io.containerd.runc.v2"
  71. )
  72. // Registration contains information for registering a plugin
  73. type Registration struct {
  74. // Type of the plugin
  75. Type Type
  76. // ID of the plugin
  77. ID string
  78. // Config specific to the plugin
  79. Config interface{}
  80. // Requires is a list of plugins that the registered plugin requires to be available
  81. Requires []Type
  82. // InitFn is called when initializing a plugin. The registration and
  83. // context are passed in. The init function may modify the registration to
  84. // add exports, capabilities and platform support declarations.
  85. InitFn func(*InitContext) (interface{}, error)
  86. }
  87. // Init the registered plugin
  88. func (r *Registration) Init(ic *InitContext) *Plugin {
  89. p, err := r.InitFn(ic)
  90. return &Plugin{
  91. Registration: r,
  92. Config: ic.Config,
  93. Meta: ic.Meta,
  94. instance: p,
  95. err: err,
  96. }
  97. }
  98. // URI returns the full plugin URI
  99. func (r *Registration) URI() string {
  100. return fmt.Sprintf("%s.%s", r.Type, r.ID)
  101. }
  102. // Service allows GRPC services to be registered with the underlying server
  103. type Service interface {
  104. Register(*grpc.Server) error
  105. }
  106. var register = struct {
  107. sync.RWMutex
  108. r []*Registration
  109. }{}
  110. // Load loads all plugins at the provided path into containerd
  111. func Load(path string) (err error) {
  112. defer func() {
  113. if v := recover(); v != nil {
  114. rerr, ok := v.(error)
  115. if !ok {
  116. rerr = fmt.Errorf("%s", v)
  117. }
  118. err = rerr
  119. }
  120. }()
  121. return loadPlugins(path)
  122. }
  123. // Register allows plugins to register
  124. func Register(r *Registration) {
  125. register.Lock()
  126. defer register.Unlock()
  127. if r.Type == "" {
  128. panic(ErrNoType)
  129. }
  130. if r.ID == "" {
  131. panic(ErrNoPluginID)
  132. }
  133. var last bool
  134. for _, requires := range r.Requires {
  135. if requires == "*" {
  136. last = true
  137. }
  138. }
  139. if last && len(r.Requires) != 1 {
  140. panic(ErrInvalidRequires)
  141. }
  142. register.r = append(register.r, r)
  143. }
  144. // Graph returns an ordered list of registered plugins for initialization.
  145. // Plugins in disableList specified by id will be disabled.
  146. func Graph(disableList []string) (ordered []*Registration) {
  147. register.RLock()
  148. defer register.RUnlock()
  149. for _, d := range disableList {
  150. for i, r := range register.r {
  151. if r.ID == d {
  152. register.r = append(register.r[:i], register.r[i+1:]...)
  153. break
  154. }
  155. }
  156. }
  157. added := map[*Registration]bool{}
  158. for _, r := range register.r {
  159. children(r.ID, r.Requires, added, &ordered)
  160. if !added[r] {
  161. ordered = append(ordered, r)
  162. added[r] = true
  163. }
  164. }
  165. return ordered
  166. }
  167. func children(id string, types []Type, added map[*Registration]bool, ordered *[]*Registration) {
  168. for _, t := range types {
  169. for _, r := range register.r {
  170. if r.ID != id && (t == "*" || r.Type == t) {
  171. children(r.ID, r.Requires, added, ordered)
  172. if !added[r] {
  173. *ordered = append(*ordered, r)
  174. added[r] = true
  175. }
  176. }
  177. }
  178. }
  179. }