store.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package 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. "github.com/docker/docker/plugin/v2"
  10. "github.com/pkg/errors"
  11. "github.com/sirupsen/logrus"
  12. )
  13. /* allowV1PluginsFallback determines daemon's support for V1 plugins.
  14. * When the time comes to remove support for V1 plugins, flipping
  15. * this bool is all that will be needed.
  16. */
  17. const allowV1PluginsFallback bool = true
  18. /* defaultAPIVersion is the version of the plugin API for volume, network,
  19. IPAM and authz. This is a very stable API. When we update this API, then
  20. pluginType should include a version. e.g. "networkdriver/2.0".
  21. */
  22. const defaultAPIVersion string = "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. ps.plugins = plugins
  57. }
  58. func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
  59. ps.RLock()
  60. defer ps.RUnlock()
  61. result := make([]plugingetter.CompatPlugin, 0, 1)
  62. for _, p := range ps.plugins {
  63. if p.IsEnabled() {
  64. if _, err := p.FilterByCap(capability); err == nil {
  65. result = append(result, p)
  66. }
  67. }
  68. }
  69. return result
  70. }
  71. // SetState sets the active state of the plugin and updates plugindb.
  72. func (ps *Store) SetState(p *v2.Plugin, state bool) {
  73. ps.Lock()
  74. defer ps.Unlock()
  75. p.PluginObj.Enabled = state
  76. }
  77. // Add adds a plugin to memory and plugindb.
  78. // An error will be returned if there is a collision.
  79. func (ps *Store) Add(p *v2.Plugin) error {
  80. ps.Lock()
  81. defer ps.Unlock()
  82. if v, exist := ps.plugins[p.GetID()]; exist {
  83. return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
  84. }
  85. ps.plugins[p.GetID()] = p
  86. return nil
  87. }
  88. // Remove removes a plugin from memory and plugindb.
  89. func (ps *Store) Remove(p *v2.Plugin) {
  90. ps.Lock()
  91. delete(ps.plugins, p.GetID())
  92. ps.Unlock()
  93. }
  94. // Get returns an enabled plugin matching the given name and capability.
  95. func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
  96. // Lookup using new model.
  97. if ps != nil {
  98. p, err := ps.GetV2Plugin(name)
  99. if err == nil {
  100. if p.IsEnabled() {
  101. fp, err := p.FilterByCap(capability)
  102. if err != nil {
  103. return nil, err
  104. }
  105. p.AddRefCount(mode)
  106. return fp, nil
  107. }
  108. // Plugin was found but it is disabled, so we should not fall back to legacy plugins
  109. // but we should error out right away
  110. return nil, errDisabled(name)
  111. }
  112. if _, ok := errors.Cause(err).(errNotFound); !ok {
  113. return nil, err
  114. }
  115. }
  116. if !allowV1PluginsFallback {
  117. return nil, errNotFound(name)
  118. }
  119. p, err := plugins.Get(name, capability)
  120. if err == nil {
  121. return p, nil
  122. }
  123. if errors.Cause(err) == plugins.ErrNotFound {
  124. return nil, errNotFound(name)
  125. }
  126. return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
  127. }
  128. // GetAllManagedPluginsByCap returns a list of managed plugins matching the given capability.
  129. func (ps *Store) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
  130. return ps.getAllByCap(capability)
  131. }
  132. // GetAllByCap returns a list of enabled plugins matching the given capability.
  133. func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
  134. result := make([]plugingetter.CompatPlugin, 0, 1)
  135. /* Daemon start always calls plugin.Init thereby initializing a store.
  136. * So store on experimental builds can never be nil, even while
  137. * handling legacy plugins. However, there are legacy plugin unit
  138. * tests where the volume subsystem directly talks with the plugin,
  139. * bypassing the daemon. For such tests, this check is necessary.
  140. */
  141. if ps != nil {
  142. ps.RLock()
  143. result = ps.getAllByCap(capability)
  144. ps.RUnlock()
  145. }
  146. // Lookup with legacy model
  147. if allowV1PluginsFallback {
  148. pl, err := plugins.GetAll(capability)
  149. if err != nil {
  150. return nil, errors.Wrap(errdefs.System(err), "legacy plugin")
  151. }
  152. for _, p := range pl {
  153. result = append(result, p)
  154. }
  155. }
  156. return result, nil
  157. }
  158. // Handle sets a callback for a given capability. It is only used by network
  159. // and ipam drivers during plugin registration. The callback registers the
  160. // driver with the subsystem (network, ipam).
  161. func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
  162. pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
  163. // Register callback with new plugin model.
  164. ps.Lock()
  165. handlers, ok := ps.handlers[pluginType]
  166. if !ok {
  167. handlers = []func(string, *plugins.Client){}
  168. }
  169. handlers = append(handlers, callback)
  170. ps.handlers[pluginType] = handlers
  171. ps.Unlock()
  172. // Register callback with legacy plugin model.
  173. if allowV1PluginsFallback {
  174. plugins.Handle(capability, callback)
  175. }
  176. }
  177. // CallHandler calls the registered callback. It is invoked during plugin enable.
  178. func (ps *Store) CallHandler(p *v2.Plugin) {
  179. for _, typ := range p.GetTypes() {
  180. for _, handler := range ps.handlers[typ.String()] {
  181. handler(p.Name(), p.Client())
  182. }
  183. }
  184. }
  185. func (ps *Store) resolvePluginID(idOrName string) (string, error) {
  186. ps.RLock() // todo: fix
  187. defer ps.RUnlock()
  188. if validFullID.MatchString(idOrName) {
  189. return idOrName, nil
  190. }
  191. ref, err := reference.ParseNormalizedNamed(idOrName)
  192. if err != nil {
  193. return "", errors.WithStack(errNotFound(idOrName))
  194. }
  195. if _, ok := ref.(reference.Canonical); ok {
  196. logrus.Warnf("canonical references cannot be resolved: %v", reference.FamiliarString(ref))
  197. return "", errors.WithStack(errNotFound(idOrName))
  198. }
  199. ref = reference.TagNameOnly(ref)
  200. for _, p := range ps.plugins {
  201. if p.PluginObj.Name == reference.FamiliarString(ref) {
  202. return p.PluginObj.ID, nil
  203. }
  204. }
  205. var found *v2.Plugin
  206. for id, p := range ps.plugins { // this can be optimized
  207. if strings.HasPrefix(id, idOrName) {
  208. if found != nil {
  209. return "", errors.WithStack(errAmbiguous(idOrName))
  210. }
  211. found = p
  212. }
  213. }
  214. if found == nil {
  215. return "", errors.WithStack(errNotFound(idOrName))
  216. }
  217. return found.PluginObj.ID, nil
  218. }