store.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package store
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/pkg/ioutils"
  8. "github.com/docker/docker/pkg/plugingetter"
  9. "github.com/docker/docker/pkg/plugins"
  10. "github.com/docker/docker/plugin/v2"
  11. "github.com/docker/docker/reference"
  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. eg "networkdriver/2.0".
  21. */
  22. const defaultAPIVersion string = "1.0"
  23. // ErrNotFound indicates that a plugin was not found locally.
  24. type ErrNotFound string
  25. func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
  26. // ErrAmbiguous indicates that a plugin was not found locally.
  27. type ErrAmbiguous string
  28. func (name ErrAmbiguous) Error() string {
  29. return fmt.Sprintf("multiple plugins found for %q", string(name))
  30. }
  31. // GetByName retreives a plugin by name.
  32. func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
  33. ps.RLock()
  34. defer ps.RUnlock()
  35. id, nameOk := ps.nameToID[name]
  36. if !nameOk {
  37. return nil, ErrNotFound(name)
  38. }
  39. p, idOk := ps.plugins[id]
  40. if !idOk {
  41. return nil, ErrNotFound(id)
  42. }
  43. return p, nil
  44. }
  45. // GetByID retreives a plugin by ID.
  46. func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
  47. ps.RLock()
  48. defer ps.RUnlock()
  49. p, idOk := ps.plugins[id]
  50. if !idOk {
  51. return nil, ErrNotFound(id)
  52. }
  53. return p, nil
  54. }
  55. // GetAll retreives all plugins.
  56. func (ps *Store) GetAll() map[string]*v2.Plugin {
  57. ps.RLock()
  58. defer ps.RUnlock()
  59. return ps.plugins
  60. }
  61. // SetAll initialized plugins during daemon restore.
  62. func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
  63. ps.Lock()
  64. defer ps.Unlock()
  65. ps.plugins = plugins
  66. }
  67. func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
  68. ps.RLock()
  69. defer ps.RUnlock()
  70. result := make([]plugingetter.CompatPlugin, 0, 1)
  71. for _, p := range ps.plugins {
  72. if p.IsEnabled() {
  73. if _, err := p.FilterByCap(capability); err == nil {
  74. result = append(result, p)
  75. }
  76. }
  77. }
  78. return result
  79. }
  80. // SetState sets the active state of the plugin and updates plugindb.
  81. func (ps *Store) SetState(p *v2.Plugin, state bool) {
  82. ps.Lock()
  83. defer ps.Unlock()
  84. p.PluginObj.Enabled = state
  85. ps.updatePluginDB()
  86. }
  87. // Add adds a plugin to memory and plugindb.
  88. // An error will be returned if there is a collision.
  89. func (ps *Store) Add(p *v2.Plugin) error {
  90. ps.Lock()
  91. defer ps.Unlock()
  92. if v, exist := ps.plugins[p.GetID()]; exist {
  93. return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
  94. }
  95. if _, exist := ps.nameToID[p.Name()]; exist {
  96. return fmt.Errorf("plugin %q already exists", p.Name())
  97. }
  98. ps.plugins[p.GetID()] = p
  99. ps.nameToID[p.Name()] = p.GetID()
  100. ps.updatePluginDB()
  101. return nil
  102. }
  103. // Update updates a plugin to memory and plugindb.
  104. func (ps *Store) Update(p *v2.Plugin) {
  105. ps.Lock()
  106. defer ps.Unlock()
  107. ps.plugins[p.GetID()] = p
  108. ps.nameToID[p.Name()] = p.GetID()
  109. ps.updatePluginDB()
  110. }
  111. // Remove removes a plugin from memory and plugindb.
  112. func (ps *Store) Remove(p *v2.Plugin) {
  113. ps.Lock()
  114. delete(ps.plugins, p.GetID())
  115. delete(ps.nameToID, p.Name())
  116. ps.updatePluginDB()
  117. ps.Unlock()
  118. }
  119. // Callers are expected to hold the store lock.
  120. func (ps *Store) updatePluginDB() error {
  121. jsonData, err := json.Marshal(ps.plugins)
  122. if err != nil {
  123. logrus.Debugf("Error in json.Marshal: %v", err)
  124. return err
  125. }
  126. ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
  127. return nil
  128. }
  129. // Get returns an enabled plugin matching the given name and capability.
  130. func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
  131. var (
  132. p *v2.Plugin
  133. err error
  134. )
  135. // Lookup using new model.
  136. if ps != nil {
  137. fullName := name
  138. if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
  139. if reference.IsNameOnly(named) {
  140. named = reference.WithDefaultTag(named)
  141. }
  142. ref, ok := named.(reference.NamedTagged)
  143. if !ok {
  144. return nil, fmt.Errorf("invalid name: %s", named.String())
  145. }
  146. fullName = ref.String()
  147. }
  148. p, err = ps.GetByName(fullName)
  149. if err == nil {
  150. p.Lock()
  151. p.RefCount += mode
  152. p.Unlock()
  153. if p.IsEnabled() {
  154. return p.FilterByCap(capability)
  155. }
  156. // Plugin was found but it is disabled, so we should not fall back to legacy plugins
  157. // but we should error out right away
  158. return nil, ErrNotFound(fullName)
  159. }
  160. if _, ok := err.(ErrNotFound); !ok {
  161. return nil, err
  162. }
  163. }
  164. // Lookup using legacy model.
  165. if allowV1PluginsFallback {
  166. p, err := plugins.Get(name, capability)
  167. if err != nil {
  168. return nil, fmt.Errorf("legacy plugin: %v", err)
  169. }
  170. return p, nil
  171. }
  172. return nil, err
  173. }
  174. // GetAllByCap returns a list of enabled plugins matching the given capability.
  175. func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
  176. result := make([]plugingetter.CompatPlugin, 0, 1)
  177. /* Daemon start always calls plugin.Init thereby initializing a store.
  178. * So store on experimental builds can never be nil, even while
  179. * handling legacy plugins. However, there are legacy plugin unit
  180. * tests where the volume subsystem directly talks with the plugin,
  181. * bypassing the daemon. For such tests, this check is necessary.
  182. */
  183. if ps != nil {
  184. ps.RLock()
  185. result = ps.getAllByCap(capability)
  186. ps.RUnlock()
  187. }
  188. // Lookup with legacy model
  189. if allowV1PluginsFallback {
  190. pl, err := plugins.GetAll(capability)
  191. if err != nil {
  192. return nil, fmt.Errorf("legacy plugin: %v", err)
  193. }
  194. for _, p := range pl {
  195. result = append(result, p)
  196. }
  197. }
  198. return result, nil
  199. }
  200. // Handle sets a callback for a given capability. It is only used by network
  201. // and ipam drivers during plugin registration. The callback registers the
  202. // driver with the subsystem (network, ipam).
  203. func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
  204. pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
  205. // Register callback with new plugin model.
  206. ps.Lock()
  207. handlers, ok := ps.handlers[pluginType]
  208. if !ok {
  209. handlers = []func(string, *plugins.Client){}
  210. }
  211. handlers = append(handlers, callback)
  212. ps.handlers[pluginType] = handlers
  213. ps.Unlock()
  214. // Register callback with legacy plugin model.
  215. if allowV1PluginsFallback {
  216. plugins.Handle(capability, callback)
  217. }
  218. }
  219. // CallHandler calls the registered callback. It is invoked during plugin enable.
  220. func (ps *Store) CallHandler(p *v2.Plugin) {
  221. for _, typ := range p.GetTypes() {
  222. for _, handler := range ps.handlers[typ.String()] {
  223. handler(p.Name(), p.Client())
  224. }
  225. }
  226. }
  227. // Search retreives a plugin by ID Prefix
  228. // If no plugin is found, then ErrNotFound is returned
  229. // If multiple plugins are found, then ErrAmbiguous is returned
  230. func (ps *Store) Search(partialID string) (*v2.Plugin, error) {
  231. ps.RLock()
  232. defer ps.RUnlock()
  233. var found *v2.Plugin
  234. for id, p := range ps.plugins {
  235. if strings.HasPrefix(id, partialID) {
  236. if found != nil {
  237. return nil, ErrAmbiguous(partialID)
  238. }
  239. found = p
  240. }
  241. }
  242. if found == nil {
  243. return nil, ErrNotFound(partialID)
  244. }
  245. return found, nil
  246. }