store.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. package plugin // import "github.com/docker/docker/plugin"
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "github.com/containerd/log"
  7. "github.com/distribution/reference"
  8. "github.com/docker/docker/errdefs"
  9. "github.com/docker/docker/pkg/plugingetter"
  10. "github.com/docker/docker/pkg/plugins"
  11. v2 "github.com/docker/docker/plugin/v2"
  12. specs "github.com/opencontainers/runtime-spec/specs-go"
  13. "github.com/pkg/errors"
  14. )
  15. // allowV1PluginsFallback determines daemon's support for V1 plugins.
  16. // When the time comes to remove support for V1 plugins, flipping
  17. // this bool is all that will be needed.
  18. const allowV1PluginsFallback = true
  19. // defaultAPIVersion is the version of the plugin API for volume, network,
  20. // IPAM and authz. This is a very stable API. When we update this API, then
  21. // pluginType should include a version. e.g. "networkdriver/2.0".
  22. const defaultAPIVersion = "1.0"
  23. // GetV2Plugin retrieves a plugin by name, id or partial ID.
  24. func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) {
  25. ps.RLock()
  26. defer ps.RUnlock()
  27. id, err := ps.resolvePluginID(refOrID)
  28. if err != nil {
  29. return nil, err
  30. }
  31. p, idOk := ps.plugins[id]
  32. if !idOk {
  33. return nil, errors.WithStack(errNotFound(id))
  34. }
  35. return p, nil
  36. }
  37. // validateName returns error if name is already reserved. always call with lock and full name
  38. func (ps *Store) validateName(name string) error {
  39. for _, p := range ps.plugins {
  40. if p.Name() == name {
  41. return alreadyExistsError(name)
  42. }
  43. }
  44. return nil
  45. }
  46. // GetAll retrieves all plugins.
  47. func (ps *Store) GetAll() map[string]*v2.Plugin {
  48. ps.RLock()
  49. defer ps.RUnlock()
  50. return ps.plugins
  51. }
  52. // SetAll initialized plugins during daemon restore.
  53. func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
  54. ps.Lock()
  55. defer ps.Unlock()
  56. for _, p := range plugins {
  57. ps.setSpecOpts(p)
  58. }
  59. ps.plugins = plugins
  60. }
  61. func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
  62. ps.RLock()
  63. defer ps.RUnlock()
  64. result := make([]plugingetter.CompatPlugin, 0, 1)
  65. for _, p := range ps.plugins {
  66. if p.IsEnabled() {
  67. if _, err := p.FilterByCap(capability); err == nil {
  68. result = append(result, p)
  69. }
  70. }
  71. }
  72. return result
  73. }
  74. // SetState sets the active state of the plugin and updates plugindb.
  75. func (ps *Store) SetState(p *v2.Plugin, state bool) {
  76. ps.Lock()
  77. defer ps.Unlock()
  78. p.PluginObj.Enabled = state
  79. }
  80. func (ps *Store) setSpecOpts(p *v2.Plugin) {
  81. var specOpts []SpecOpt
  82. for _, typ := range p.GetTypes() {
  83. opts, ok := ps.specOpts[typ.String()]
  84. if ok {
  85. specOpts = append(specOpts, opts...)
  86. }
  87. }
  88. p.SetSpecOptModifier(func(s *specs.Spec) {
  89. for _, o := range specOpts {
  90. o(s)
  91. }
  92. })
  93. }
  94. // Add adds a plugin to memory and plugindb.
  95. // An error will be returned if there is a collision.
  96. func (ps *Store) Add(p *v2.Plugin) error {
  97. ps.Lock()
  98. defer ps.Unlock()
  99. if v, exist := ps.plugins[p.GetID()]; exist {
  100. return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
  101. }
  102. ps.setSpecOpts(p)
  103. ps.plugins[p.GetID()] = p
  104. return nil
  105. }
  106. // Remove removes a plugin from memory and plugindb.
  107. func (ps *Store) Remove(p *v2.Plugin) {
  108. ps.Lock()
  109. delete(ps.plugins, p.GetID())
  110. ps.Unlock()
  111. }
  112. // Get returns an enabled plugin matching the given name and capability.
  113. func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
  114. // Lookup using new model.
  115. if ps != nil {
  116. p, err := ps.GetV2Plugin(name)
  117. if err == nil {
  118. if p.IsEnabled() {
  119. fp, err := p.FilterByCap(capability)
  120. if err != nil {
  121. return nil, err
  122. }
  123. p.AddRefCount(mode)
  124. return fp, nil
  125. }
  126. // Plugin was found but it is disabled, so we should not fall back to legacy plugins
  127. // but we should error out right away
  128. return nil, errDisabled(name)
  129. }
  130. var ierr errNotFound
  131. if !errors.As(err, &ierr) {
  132. return nil, err
  133. }
  134. }
  135. if !allowV1PluginsFallback {
  136. return nil, errNotFound(name)
  137. }
  138. p, err := plugins.Get(name, capability)
  139. if err == nil {
  140. return p, nil
  141. }
  142. if errors.Is(err, plugins.ErrNotFound) {
  143. return nil, errNotFound(name)
  144. }
  145. return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
  146. }
  147. // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability.
  148. func (ps *Store) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
  149. return ps.getAllByCap(capability)
  150. }
  151. // GetAllByCap returns a list of enabled plugins matching the given capability.
  152. func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
  153. result := make([]plugingetter.CompatPlugin, 0, 1)
  154. /* Daemon start always calls plugin.Init thereby initializing a store.
  155. * So store on experimental builds can never be nil, even while
  156. * handling legacy plugins. However, there are legacy plugin unit
  157. * tests where the volume subsystem directly talks with the plugin,
  158. * bypassing the daemon. For such tests, this check is necessary.
  159. */
  160. if ps != nil {
  161. result = ps.getAllByCap(capability)
  162. }
  163. // Lookup with legacy model
  164. if allowV1PluginsFallback {
  165. l := plugins.NewLocalRegistry()
  166. pl, err := l.GetAll(capability)
  167. if err != nil {
  168. return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
  169. }
  170. for _, p := range pl {
  171. result = append(result, p)
  172. }
  173. }
  174. return result, nil
  175. }
  176. func pluginType(cap string) string {
  177. return fmt.Sprintf("docker.%s/%s", strings.ToLower(cap), defaultAPIVersion)
  178. }
  179. // Handle sets a callback for a given capability. It is only used by network
  180. // and ipam drivers during plugin registration. The callback registers the
  181. // driver with the subsystem (network, ipam).
  182. func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
  183. typ := pluginType(capability)
  184. // Register callback with new plugin model.
  185. ps.Lock()
  186. handlers, ok := ps.handlers[typ]
  187. if !ok {
  188. handlers = []func(string, *plugins.Client){}
  189. }
  190. handlers = append(handlers, callback)
  191. ps.handlers[typ] = handlers
  192. ps.Unlock()
  193. // Register callback with legacy plugin model.
  194. if allowV1PluginsFallback {
  195. plugins.Handle(capability, callback)
  196. }
  197. }
  198. // RegisterRuntimeOpt stores a list of SpecOpts for the provided capability.
  199. // These options are applied to the runtime spec before a plugin is started for the specified capability.
  200. func (ps *Store) RegisterRuntimeOpt(cap string, opts ...SpecOpt) {
  201. ps.Lock()
  202. defer ps.Unlock()
  203. typ := pluginType(cap)
  204. ps.specOpts[typ] = append(ps.specOpts[typ], opts...)
  205. }
  206. // CallHandler calls the registered callback. It is invoked during plugin enable.
  207. func (ps *Store) CallHandler(p *v2.Plugin) {
  208. for _, typ := range p.GetTypes() {
  209. for _, handler := range ps.handlers[typ.String()] {
  210. handler(p.Name(), p.Client())
  211. }
  212. }
  213. }
  214. // resolvePluginID must be protected by ps.RLock
  215. func (ps *Store) resolvePluginID(idOrName string) (string, error) {
  216. if validFullID.MatchString(idOrName) {
  217. return idOrName, nil
  218. }
  219. ref, err := reference.ParseNormalizedNamed(idOrName)
  220. if err != nil {
  221. return "", errors.WithStack(errNotFound(idOrName))
  222. }
  223. if _, ok := ref.(reference.Canonical); ok {
  224. log.G(context.TODO()).Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
  225. return "", errors.WithStack(errNotFound(idOrName))
  226. }
  227. ref = reference.TagNameOnly(ref)
  228. for _, p := range ps.plugins {
  229. if p.PluginObj.Name == reference.FamiliarString(ref) {
  230. return p.PluginObj.ID, nil
  231. }
  232. }
  233. var found *v2.Plugin
  234. for id, p := range ps.plugins { // this can be optimized
  235. if strings.HasPrefix(id, idOrName) {
  236. if found != nil {
  237. return "", errors.WithStack(errAmbiguous(idOrName))
  238. }
  239. found = p
  240. }
  241. }
  242. if found == nil {
  243. return "", errors.WithStack(errNotFound(idOrName))
  244. }
  245. return found.PluginObj.ID, nil
  246. }