store.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. package plugin // import "github.com/docker/docker/plugin"
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/docker/distribution/reference"
  6. "github.com/docker/docker/errdefs"
  7. "github.com/docker/docker/pkg/plugingetter"
  8. "github.com/docker/docker/pkg/plugins"
  9. v2 "github.com/docker/docker/plugin/v2"
  10. specs "github.com/opencontainers/runtime-spec/specs-go"
  11. "github.com/pkg/errors"
  12. "github.com/sirupsen/logrus"
  13. )
  14. /* allowV1PluginsFallback determines daemon's support for V1 plugins.
  15. * When the time comes to remove support for V1 plugins, flipping
  16. * this bool is all that will be needed.
  17. */
  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. */
  23. const defaultAPIVersion = "1.0"
  24. // GetV2Plugin retrieves a plugin by name, id or partial ID.
  25. func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) {
  26. ps.RLock()
  27. defer ps.RUnlock()
  28. id, err := ps.resolvePluginID(refOrID)
  29. if err != nil {
  30. return nil, err
  31. }
  32. p, idOk := ps.plugins[id]
  33. if !idOk {
  34. return nil, errors.WithStack(errNotFound(id))
  35. }
  36. return p, nil
  37. }
  38. // validateName returns error if name is already reserved. always call with lock and full name
  39. func (ps *Store) validateName(name string) error {
  40. for _, p := range ps.plugins {
  41. if p.Name() == name {
  42. return alreadyExistsError(name)
  43. }
  44. }
  45. return nil
  46. }
  47. // GetAll retrieves all plugins.
  48. func (ps *Store) GetAll() map[string]*v2.Plugin {
  49. ps.RLock()
  50. defer ps.RUnlock()
  51. return ps.plugins
  52. }
  53. // SetAll initialized plugins during daemon restore.
  54. func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
  55. ps.Lock()
  56. defer ps.Unlock()
  57. for _, p := range plugins {
  58. ps.setSpecOpts(p)
  59. }
  60. ps.plugins = plugins
  61. }
  62. func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
  63. ps.RLock()
  64. defer ps.RUnlock()
  65. result := make([]plugingetter.CompatPlugin, 0, 1)
  66. for _, p := range ps.plugins {
  67. if p.IsEnabled() {
  68. if _, err := p.FilterByCap(capability); err == nil {
  69. result = append(result, p)
  70. }
  71. }
  72. }
  73. return result
  74. }
  75. // SetState sets the active state of the plugin and updates plugindb.
  76. func (ps *Store) SetState(p *v2.Plugin, state bool) {
  77. ps.Lock()
  78. defer ps.Unlock()
  79. p.PluginObj.Enabled = state
  80. }
  81. func (ps *Store) setSpecOpts(p *v2.Plugin) {
  82. var specOpts []SpecOpt
  83. for _, typ := range p.GetTypes() {
  84. opts, ok := ps.specOpts[typ.String()]
  85. if ok {
  86. specOpts = append(specOpts, opts...)
  87. }
  88. }
  89. p.SetSpecOptModifier(func(s *specs.Spec) {
  90. for _, o := range specOpts {
  91. o(s)
  92. }
  93. })
  94. }
  95. // Add adds a plugin to memory and plugindb.
  96. // An error will be returned if there is a collision.
  97. func (ps *Store) Add(p *v2.Plugin) error {
  98. ps.Lock()
  99. defer ps.Unlock()
  100. if v, exist := ps.plugins[p.GetID()]; exist {
  101. return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
  102. }
  103. ps.setSpecOpts(p)
  104. ps.plugins[p.GetID()] = p
  105. return nil
  106. }
  107. // Remove removes a plugin from memory and plugindb.
  108. func (ps *Store) Remove(p *v2.Plugin) {
  109. ps.Lock()
  110. delete(ps.plugins, p.GetID())
  111. ps.Unlock()
  112. }
  113. // Get returns an enabled plugin matching the given name and capability.
  114. func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
  115. // Lookup using new model.
  116. if ps != nil {
  117. p, err := ps.GetV2Plugin(name)
  118. if err == nil {
  119. if p.IsEnabled() {
  120. fp, err := p.FilterByCap(capability)
  121. if err != nil {
  122. return nil, err
  123. }
  124. p.AddRefCount(mode)
  125. return fp, nil
  126. }
  127. // Plugin was found but it is disabled, so we should not fall back to legacy plugins
  128. // but we should error out right away
  129. return nil, errDisabled(name)
  130. }
  131. var ierr errNotFound
  132. if !errors.As(err, &ierr) {
  133. return nil, err
  134. }
  135. }
  136. if !allowV1PluginsFallback {
  137. return nil, errNotFound(name)
  138. }
  139. p, err := plugins.Get(name, capability)
  140. if err == nil {
  141. return p, nil
  142. }
  143. if errors.Is(err, plugins.ErrNotFound) {
  144. return nil, errNotFound(name)
  145. }
  146. return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
  147. }
  148. // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability.
  149. func (ps *Store) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
  150. return ps.getAllByCap(capability)
  151. }
  152. // GetAllByCap returns a list of enabled plugins matching the given capability.
  153. func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
  154. result := make([]plugingetter.CompatPlugin, 0, 1)
  155. /* Daemon start always calls plugin.Init thereby initializing a store.
  156. * So store on experimental builds can never be nil, even while
  157. * handling legacy plugins. However, there are legacy plugin unit
  158. * tests where the volume subsystem directly talks with the plugin,
  159. * bypassing the daemon. For such tests, this check is necessary.
  160. */
  161. if ps != nil {
  162. result = ps.getAllByCap(capability)
  163. }
  164. // Lookup with legacy model
  165. if allowV1PluginsFallback {
  166. pl, err := plugins.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. logrus.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. }