store.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package plugin
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/docker/distribution/reference"
  6. "github.com/docker/docker/pkg/plugingetter"
  7. "github.com/docker/docker/pkg/plugins"
  8. "github.com/docker/docker/plugin/v2"
  9. "github.com/pkg/errors"
  10. "github.com/sirupsen/logrus"
  11. )
  12. /* allowV1PluginsFallback determines daemon's support for V1 plugins.
  13. * When the time comes to remove support for V1 plugins, flipping
  14. * this bool is all that will be needed.
  15. */
  16. const allowV1PluginsFallback bool = true
  17. /* defaultAPIVersion is the version of the plugin API for volume, network,
  18. IPAM and authz. This is a very stable API. When we update this API, then
  19. pluginType should include a version. e.g. "networkdriver/2.0".
  20. */
  21. const defaultAPIVersion string = "1.0"
  22. // GetV2Plugin retrieves a plugin by name, id or partial ID.
  23. func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) {
  24. ps.RLock()
  25. defer ps.RUnlock()
  26. id, err := ps.resolvePluginID(refOrID)
  27. if err != nil {
  28. return nil, err
  29. }
  30. p, idOk := ps.plugins[id]
  31. if !idOk {
  32. return nil, errors.WithStack(errNotFound(id))
  33. }
  34. return p, nil
  35. }
  36. // validateName returns error if name is already reserved. always call with lock and full name
  37. func (ps *Store) validateName(name string) error {
  38. for _, p := range ps.plugins {
  39. if p.Name() == name {
  40. return alreadyExistsError(name)
  41. }
  42. }
  43. return nil
  44. }
  45. // GetAll retrieves all plugins.
  46. func (ps *Store) GetAll() map[string]*v2.Plugin {
  47. ps.RLock()
  48. defer ps.RUnlock()
  49. return ps.plugins
  50. }
  51. // SetAll initialized plugins during daemon restore.
  52. func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
  53. ps.Lock()
  54. defer ps.Unlock()
  55. ps.plugins = plugins
  56. }
  57. func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
  58. ps.RLock()
  59. defer ps.RUnlock()
  60. result := make([]plugingetter.CompatPlugin, 0, 1)
  61. for _, p := range ps.plugins {
  62. if p.IsEnabled() {
  63. if _, err := p.FilterByCap(capability); err == nil {
  64. result = append(result, p)
  65. }
  66. }
  67. }
  68. return result
  69. }
  70. // SetState sets the active state of the plugin and updates plugindb.
  71. func (ps *Store) SetState(p *v2.Plugin, state bool) {
  72. ps.Lock()
  73. defer ps.Unlock()
  74. p.PluginObj.Enabled = state
  75. }
  76. // Add adds a plugin to memory and plugindb.
  77. // An error will be returned if there is a collision.
  78. func (ps *Store) Add(p *v2.Plugin) error {
  79. ps.Lock()
  80. defer ps.Unlock()
  81. if v, exist := ps.plugins[p.GetID()]; exist {
  82. return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
  83. }
  84. ps.plugins[p.GetID()] = p
  85. return nil
  86. }
  87. // Remove removes a plugin from memory and plugindb.
  88. func (ps *Store) Remove(p *v2.Plugin) {
  89. ps.Lock()
  90. delete(ps.plugins, p.GetID())
  91. ps.Unlock()
  92. }
  93. // Get returns an enabled plugin matching the given name and capability.
  94. func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
  95. var (
  96. p *v2.Plugin
  97. err error
  98. )
  99. // Lookup using new model.
  100. if ps != nil {
  101. p, err = ps.GetV2Plugin(name)
  102. if err == nil {
  103. p.AddRefCount(mode)
  104. if p.IsEnabled() {
  105. return p.FilterByCap(capability)
  106. }
  107. // Plugin was found but it is disabled, so we should not fall back to legacy plugins
  108. // but we should error out right away
  109. return nil, errDisabled(name)
  110. }
  111. if _, ok := errors.Cause(err).(errNotFound); !ok {
  112. return nil, err
  113. }
  114. }
  115. // Lookup using legacy model.
  116. if allowV1PluginsFallback {
  117. p, err := plugins.Get(name, capability)
  118. if err != nil {
  119. if errors.Cause(err) == plugins.ErrNotFound {
  120. return nil, errNotFound(name)
  121. }
  122. return nil, errors.Wrap(systemError{err}, "legacy plugin")
  123. }
  124. return p, nil
  125. }
  126. return nil, err
  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(systemError{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. }