backend_linux.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // +build linux
  2. package plugin
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "os"
  11. "path/filepath"
  12. "regexp"
  13. "github.com/Sirupsen/logrus"
  14. "github.com/docker/docker/api/types"
  15. "github.com/docker/docker/pkg/archive"
  16. "github.com/docker/docker/pkg/chrootarchive"
  17. "github.com/docker/docker/pkg/stringid"
  18. "github.com/docker/docker/plugin/distribution"
  19. "github.com/docker/docker/plugin/v2"
  20. "github.com/docker/docker/reference"
  21. "golang.org/x/net/context"
  22. )
  23. var (
  24. validFullID = regexp.MustCompile(`^([a-f0-9]{64})$`)
  25. validPartialID = regexp.MustCompile(`^([a-f0-9]{1,64})$`)
  26. )
  27. // Disable deactivates a plugin, which implies that they cannot be used by containers.
  28. func (pm *Manager) Disable(name string) error {
  29. p, err := pm.pluginStore.GetByName(name)
  30. if err != nil {
  31. return err
  32. }
  33. if err := pm.disable(p); err != nil {
  34. return err
  35. }
  36. pm.pluginEventLogger(p.GetID(), name, "disable")
  37. return nil
  38. }
  39. // Enable activates a plugin, which implies that they are ready to be used by containers.
  40. func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
  41. p, err := pm.pluginStore.GetByName(name)
  42. if err != nil {
  43. return err
  44. }
  45. p.TimeoutInSecs = config.Timeout
  46. if err := pm.enable(p, false); err != nil {
  47. return err
  48. }
  49. pm.pluginEventLogger(p.GetID(), name, "enable")
  50. return nil
  51. }
  52. // Inspect examines a plugin config
  53. func (pm *Manager) Inspect(refOrID string) (tp types.Plugin, err error) {
  54. // Match on full ID
  55. if validFullID.MatchString(refOrID) {
  56. p, err := pm.pluginStore.GetByID(refOrID)
  57. if err == nil {
  58. return p.PluginObj, nil
  59. }
  60. }
  61. // Match on full name
  62. if pluginName, err := getPluginName(refOrID); err == nil {
  63. if p, err := pm.pluginStore.GetByName(pluginName); err == nil {
  64. return p.PluginObj, nil
  65. }
  66. }
  67. // Match on partial ID
  68. if validPartialID.MatchString(refOrID) {
  69. p, err := pm.pluginStore.Search(refOrID)
  70. if err == nil {
  71. return p.PluginObj, nil
  72. }
  73. return tp, err
  74. }
  75. return tp, fmt.Errorf("no plugin name or ID associated with %q", refOrID)
  76. }
  77. func (pm *Manager) pull(ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig, pluginID string) (types.PluginPrivileges, error) {
  78. pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
  79. if err != nil {
  80. logrus.Debugf("error in distribution.Pull(): %v", err)
  81. return nil, err
  82. }
  83. if err := distribution.WritePullData(pd, filepath.Join(pm.libRoot, pluginID), true); err != nil {
  84. logrus.Debugf("error in distribution.WritePullData(): %v", err)
  85. return nil, err
  86. }
  87. tag := distribution.GetTag(ref)
  88. p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, pm.libRoot, tag)
  89. if err := p.InitPlugin(); err != nil {
  90. return nil, err
  91. }
  92. pm.pluginStore.Add(p)
  93. pm.pluginEventLogger(pluginID, ref.String(), "pull")
  94. return p.ComputePrivileges(), nil
  95. }
  96. // Pull pulls a plugin and computes the privileges required to install it.
  97. func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
  98. ref, err := distribution.GetRef(name)
  99. if err != nil {
  100. logrus.Debugf("error in distribution.GetRef: %v", err)
  101. return nil, err
  102. }
  103. name = ref.String()
  104. if p, _ := pm.pluginStore.GetByName(name); p != nil {
  105. logrus.Debug("plugin already exists")
  106. return nil, fmt.Errorf("%s exists", name)
  107. }
  108. pluginID := stringid.GenerateNonCryptoID()
  109. pluginDir := filepath.Join(pm.libRoot, pluginID)
  110. if err := os.MkdirAll(pluginDir, 0755); err != nil {
  111. logrus.Debugf("error in MkdirAll: %v", err)
  112. return nil, err
  113. }
  114. priv, err := pm.pull(ref, metaHeader, authConfig, pluginID)
  115. if err != nil {
  116. if err := os.RemoveAll(pluginDir); err != nil {
  117. logrus.Warnf("unable to remove %q from failed plugin pull: %v", pluginDir, err)
  118. }
  119. return nil, err
  120. }
  121. return priv, nil
  122. }
  123. // List displays the list of plugins and associated metadata.
  124. func (pm *Manager) List() ([]types.Plugin, error) {
  125. plugins := pm.pluginStore.GetAll()
  126. out := make([]types.Plugin, 0, len(plugins))
  127. for _, p := range plugins {
  128. out = append(out, p.PluginObj)
  129. }
  130. return out, nil
  131. }
  132. // Push pushes a plugin to the store.
  133. func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.AuthConfig) error {
  134. p, err := pm.pluginStore.GetByName(name)
  135. if err != nil {
  136. return err
  137. }
  138. dest := filepath.Join(pm.libRoot, p.GetID())
  139. config, err := ioutil.ReadFile(filepath.Join(dest, "config.json"))
  140. if err != nil {
  141. return err
  142. }
  143. var dummy types.Plugin
  144. err = json.Unmarshal(config, &dummy)
  145. if err != nil {
  146. return err
  147. }
  148. rootfs, err := archive.Tar(filepath.Join(dest, "rootfs"), archive.Gzip)
  149. if err != nil {
  150. return err
  151. }
  152. defer rootfs.Close()
  153. _, err = distribution.Push(name, pm.registryService, metaHeader, authConfig, ioutil.NopCloser(bytes.NewReader(config)), rootfs)
  154. // XXX: Ignore returning digest for now.
  155. // Since digest needs to be written to the ProgressWriter.
  156. return err
  157. }
  158. // Remove deletes plugin's root directory.
  159. func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
  160. p, err := pm.pluginStore.GetByName(name)
  161. if err != nil {
  162. return err
  163. }
  164. if !config.ForceRemove {
  165. p.RLock()
  166. if p.RefCount > 0 {
  167. p.RUnlock()
  168. return fmt.Errorf("plugin %s is in use", p.Name())
  169. }
  170. p.RUnlock()
  171. if p.IsEnabled() {
  172. return fmt.Errorf("plugin %s is enabled", p.Name())
  173. }
  174. }
  175. if p.IsEnabled() {
  176. if err := pm.disable(p); err != nil {
  177. logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
  178. }
  179. }
  180. pm.pluginStore.Remove(p)
  181. os.RemoveAll(filepath.Join(pm.libRoot, p.GetID()))
  182. pm.pluginEventLogger(p.GetID(), name, "remove")
  183. return nil
  184. }
  185. // Set sets plugin args
  186. func (pm *Manager) Set(name string, args []string) error {
  187. p, err := pm.pluginStore.GetByName(name)
  188. if err != nil {
  189. return err
  190. }
  191. return p.Set(args)
  192. }
  193. // CreateFromContext creates a plugin from the given pluginDir which contains
  194. // both the rootfs and the config.json and a repoName with optional tag.
  195. func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, options *types.PluginCreateOptions) error {
  196. pluginID := stringid.GenerateNonCryptoID()
  197. pluginDir := filepath.Join(pm.libRoot, pluginID)
  198. if err := os.MkdirAll(pluginDir, 0755); err != nil {
  199. return err
  200. }
  201. // In case an error happens, remove the created directory.
  202. if err := pm.createFromContext(ctx, pluginID, pluginDir, tarCtx, options); err != nil {
  203. if err := os.RemoveAll(pluginDir); err != nil {
  204. logrus.Warnf("unable to remove %q from failed plugin creation: %v", pluginDir, err)
  205. }
  206. return err
  207. }
  208. return nil
  209. }
  210. func (pm *Manager) createFromContext(ctx context.Context, pluginID, pluginDir string, tarCtx io.Reader, options *types.PluginCreateOptions) error {
  211. if err := chrootarchive.Untar(tarCtx, pluginDir, nil); err != nil {
  212. return err
  213. }
  214. repoName := options.RepoName
  215. ref, err := distribution.GetRef(repoName)
  216. if err != nil {
  217. return err
  218. }
  219. name := ref.Name()
  220. tag := distribution.GetTag(ref)
  221. p := v2.NewPlugin(name, pluginID, pm.runRoot, pm.libRoot, tag)
  222. if err := p.InitPlugin(); err != nil {
  223. return err
  224. }
  225. if err := pm.pluginStore.Add(p); err != nil {
  226. return err
  227. }
  228. pm.pluginEventLogger(p.GetID(), repoName, "create")
  229. return nil
  230. }
  231. func getPluginName(name string) (string, error) {
  232. named, err := reference.ParseNamed(name) // FIXME: validate
  233. if err != nil {
  234. return "", err
  235. }
  236. if reference.IsNameOnly(named) {
  237. named = reference.WithDefaultTag(named)
  238. }
  239. ref, ok := named.(reference.NamedTagged)
  240. if !ok {
  241. return "", fmt.Errorf("invalid name: %s", named.String())
  242. }
  243. return ref.String(), nil
  244. }