backend_linux.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. // +build linux
  2. package plugin
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net/http"
  11. "os"
  12. "path/filepath"
  13. "reflect"
  14. "regexp"
  15. "github.com/Sirupsen/logrus"
  16. "github.com/docker/docker/api/types"
  17. "github.com/docker/docker/pkg/archive"
  18. "github.com/docker/docker/pkg/chrootarchive"
  19. "github.com/docker/docker/pkg/stringid"
  20. "github.com/docker/docker/plugin/distribution"
  21. "github.com/docker/docker/plugin/v2"
  22. "github.com/docker/docker/reference"
  23. "golang.org/x/net/context"
  24. )
  25. var (
  26. validFullID = regexp.MustCompile(`^([a-f0-9]{64})$`)
  27. validPartialID = regexp.MustCompile(`^([a-f0-9]{1,64})$`)
  28. )
  29. // Disable deactivates a plugin, which implies that they cannot be used by containers.
  30. func (pm *Manager) Disable(name string) error {
  31. p, err := pm.pluginStore.GetByName(name)
  32. if err != nil {
  33. return err
  34. }
  35. pm.mu.RLock()
  36. c := pm.cMap[p]
  37. pm.mu.RUnlock()
  38. if err := pm.disable(p, c); err != nil {
  39. return err
  40. }
  41. pm.pluginEventLogger(p.GetID(), name, "disable")
  42. return nil
  43. }
  44. // Enable activates a plugin, which implies that they are ready to be used by containers.
  45. func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
  46. p, err := pm.pluginStore.GetByName(name)
  47. if err != nil {
  48. return err
  49. }
  50. c := &controller{timeoutInSecs: config.Timeout}
  51. if err := pm.enable(p, c, false); err != nil {
  52. return err
  53. }
  54. pm.pluginEventLogger(p.GetID(), name, "enable")
  55. return nil
  56. }
  57. // Inspect examines a plugin config
  58. func (pm *Manager) Inspect(refOrID string) (tp types.Plugin, err error) {
  59. // Match on full ID
  60. if validFullID.MatchString(refOrID) {
  61. p, err := pm.pluginStore.GetByID(refOrID)
  62. if err == nil {
  63. return p.PluginObj, nil
  64. }
  65. }
  66. // Match on full name
  67. if pluginName, err := getPluginName(refOrID); err == nil {
  68. if p, err := pm.pluginStore.GetByName(pluginName); err == nil {
  69. return p.PluginObj, nil
  70. }
  71. }
  72. // Match on partial ID
  73. if validPartialID.MatchString(refOrID) {
  74. p, err := pm.pluginStore.Search(refOrID)
  75. if err == nil {
  76. return p.PluginObj, nil
  77. }
  78. return tp, err
  79. }
  80. return tp, fmt.Errorf("no such plugin name or ID associated with %q", refOrID)
  81. }
  82. func (pm *Manager) pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (reference.Named, distribution.PullData, error) {
  83. ref, err := distribution.GetRef(name)
  84. if err != nil {
  85. logrus.Debugf("error in distribution.GetRef: %v", err)
  86. return nil, nil, err
  87. }
  88. name = ref.String()
  89. if p, _ := pm.pluginStore.GetByName(name); p != nil {
  90. logrus.Debug("plugin already exists")
  91. return nil, nil, fmt.Errorf("%s exists", name)
  92. }
  93. pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
  94. if err != nil {
  95. logrus.Debugf("error in distribution.Pull(): %v", err)
  96. return nil, nil, err
  97. }
  98. return ref, pd, nil
  99. }
  100. func computePrivileges(pd distribution.PullData) (types.PluginPrivileges, error) {
  101. config, err := pd.Config()
  102. if err != nil {
  103. return nil, err
  104. }
  105. var c types.PluginConfig
  106. if err := json.Unmarshal(config, &c); err != nil {
  107. return nil, err
  108. }
  109. var privileges types.PluginPrivileges
  110. if c.Network.Type != "null" && c.Network.Type != "bridge" && c.Network.Type != "" {
  111. privileges = append(privileges, types.PluginPrivilege{
  112. Name: "network",
  113. Description: "permissions to access a network",
  114. Value: []string{c.Network.Type},
  115. })
  116. }
  117. for _, mount := range c.Mounts {
  118. if mount.Source != nil {
  119. privileges = append(privileges, types.PluginPrivilege{
  120. Name: "mount",
  121. Description: "host path to mount",
  122. Value: []string{*mount.Source},
  123. })
  124. }
  125. }
  126. for _, device := range c.Linux.Devices {
  127. if device.Path != nil {
  128. privileges = append(privileges, types.PluginPrivilege{
  129. Name: "device",
  130. Description: "host device to access",
  131. Value: []string{*device.Path},
  132. })
  133. }
  134. }
  135. if c.Linux.DeviceCreation {
  136. privileges = append(privileges, types.PluginPrivilege{
  137. Name: "device-creation",
  138. Description: "allow creating devices inside plugin",
  139. Value: []string{"true"},
  140. })
  141. }
  142. if len(c.Linux.Capabilities) > 0 {
  143. privileges = append(privileges, types.PluginPrivilege{
  144. Name: "capabilities",
  145. Description: "list of additional capabilities required",
  146. Value: c.Linux.Capabilities,
  147. })
  148. }
  149. return privileges, nil
  150. }
  151. // Privileges pulls a plugin config and computes the privileges required to install it.
  152. func (pm *Manager) Privileges(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
  153. _, pd, err := pm.pull(name, metaHeader, authConfig)
  154. if err != nil {
  155. return nil, err
  156. }
  157. return computePrivileges(pd)
  158. }
  159. // Pull pulls a plugin, check if the correct privileges are provided and install the plugin.
  160. func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig, privileges types.PluginPrivileges) (err error) {
  161. ref, pd, err := pm.pull(name, metaHeader, authConfig)
  162. if err != nil {
  163. return err
  164. }
  165. requiredPrivileges, err := computePrivileges(pd)
  166. if err != nil {
  167. return err
  168. }
  169. if !reflect.DeepEqual(privileges, requiredPrivileges) {
  170. return errors.New("incorrect privileges")
  171. }
  172. pluginID := stringid.GenerateNonCryptoID()
  173. pluginDir := filepath.Join(pm.libRoot, pluginID)
  174. if err := os.MkdirAll(pluginDir, 0755); err != nil {
  175. logrus.Debugf("error in MkdirAll: %v", err)
  176. return err
  177. }
  178. defer func() {
  179. if err != nil {
  180. if delErr := os.RemoveAll(pluginDir); delErr != nil {
  181. logrus.Warnf("unable to remove %q from failed plugin pull: %v", pluginDir, delErr)
  182. }
  183. }
  184. }()
  185. err = distribution.WritePullData(pd, filepath.Join(pm.libRoot, pluginID), true)
  186. if err != nil {
  187. logrus.Debugf("error in distribution.WritePullData(): %v", err)
  188. return err
  189. }
  190. tag := distribution.GetTag(ref)
  191. p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, pm.libRoot, tag)
  192. err = p.InitPlugin()
  193. if err != nil {
  194. return err
  195. }
  196. pm.pluginStore.Add(p)
  197. pm.pluginEventLogger(pluginID, ref.String(), "pull")
  198. return nil
  199. }
  200. // List displays the list of plugins and associated metadata.
  201. func (pm *Manager) List() ([]types.Plugin, error) {
  202. plugins := pm.pluginStore.GetAll()
  203. out := make([]types.Plugin, 0, len(plugins))
  204. for _, p := range plugins {
  205. out = append(out, p.PluginObj)
  206. }
  207. return out, nil
  208. }
  209. // Push pushes a plugin to the store.
  210. func (pm *Manager) Push(name string, metaHeader http.Header, authConfig *types.AuthConfig) error {
  211. p, err := pm.pluginStore.GetByName(name)
  212. if err != nil {
  213. return err
  214. }
  215. dest := filepath.Join(pm.libRoot, p.GetID())
  216. config, err := ioutil.ReadFile(filepath.Join(dest, "config.json"))
  217. if err != nil {
  218. return err
  219. }
  220. var dummy types.Plugin
  221. err = json.Unmarshal(config, &dummy)
  222. if err != nil {
  223. return err
  224. }
  225. rootfs, err := archive.Tar(p.Rootfs, archive.Gzip)
  226. if err != nil {
  227. return err
  228. }
  229. defer rootfs.Close()
  230. _, err = distribution.Push(name, pm.registryService, metaHeader, authConfig, ioutil.NopCloser(bytes.NewReader(config)), rootfs)
  231. // XXX: Ignore returning digest for now.
  232. // Since digest needs to be written to the ProgressWriter.
  233. return err
  234. }
  235. // Remove deletes plugin's root directory.
  236. func (pm *Manager) Remove(name string, config *types.PluginRmConfig) error {
  237. p, err := pm.pluginStore.GetByName(name)
  238. pm.mu.RLock()
  239. c := pm.cMap[p]
  240. pm.mu.RUnlock()
  241. if err != nil {
  242. return err
  243. }
  244. if !config.ForceRemove {
  245. if p.GetRefCount() > 0 {
  246. return fmt.Errorf("plugin %s is in use", p.Name())
  247. }
  248. if p.IsEnabled() {
  249. return fmt.Errorf("plugin %s is enabled", p.Name())
  250. }
  251. }
  252. if p.IsEnabled() {
  253. if err := pm.disable(p, c); err != nil {
  254. logrus.Errorf("failed to disable plugin '%s': %s", p.Name(), err)
  255. }
  256. }
  257. id := p.GetID()
  258. pm.pluginStore.Remove(p)
  259. pluginDir := filepath.Join(pm.libRoot, id)
  260. if err := os.RemoveAll(pluginDir); err != nil {
  261. logrus.Warnf("unable to remove %q from plugin remove: %v", pluginDir, err)
  262. }
  263. pm.pluginEventLogger(id, name, "remove")
  264. return nil
  265. }
  266. // Set sets plugin args
  267. func (pm *Manager) Set(name string, args []string) error {
  268. p, err := pm.pluginStore.GetByName(name)
  269. if err != nil {
  270. return err
  271. }
  272. return p.Set(args)
  273. }
  274. // CreateFromContext creates a plugin from the given pluginDir which contains
  275. // both the rootfs and the config.json and a repoName with optional tag.
  276. func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, options *types.PluginCreateOptions) error {
  277. repoName := options.RepoName
  278. ref, err := distribution.GetRef(repoName)
  279. if err != nil {
  280. return err
  281. }
  282. name := ref.Name()
  283. tag := distribution.GetTag(ref)
  284. pluginID := stringid.GenerateNonCryptoID()
  285. p := v2.NewPlugin(name, pluginID, pm.runRoot, pm.libRoot, tag)
  286. if v, _ := pm.pluginStore.GetByName(p.Name()); v != nil {
  287. return fmt.Errorf("plugin %q already exists", p.Name())
  288. }
  289. pluginDir := filepath.Join(pm.libRoot, pluginID)
  290. if err := os.MkdirAll(pluginDir, 0755); err != nil {
  291. return err
  292. }
  293. // In case an error happens, remove the created directory.
  294. if err := pm.createFromContext(ctx, tarCtx, pluginDir, repoName, p); err != nil {
  295. if err := os.RemoveAll(pluginDir); err != nil {
  296. logrus.Warnf("unable to remove %q from failed plugin creation: %v", pluginDir, err)
  297. }
  298. return err
  299. }
  300. return nil
  301. }
  302. func (pm *Manager) createFromContext(ctx context.Context, tarCtx io.Reader, pluginDir, repoName string, p *v2.Plugin) error {
  303. if err := chrootarchive.Untar(tarCtx, pluginDir, nil); err != nil {
  304. return err
  305. }
  306. if err := p.InitPlugin(); err != nil {
  307. return err
  308. }
  309. if err := pm.pluginStore.Add(p); err != nil {
  310. return err
  311. }
  312. pm.pluginEventLogger(p.GetID(), repoName, "create")
  313. return nil
  314. }
  315. func getPluginName(name string) (string, error) {
  316. named, err := reference.ParseNamed(name) // FIXME: validate
  317. if err != nil {
  318. return "", err
  319. }
  320. if reference.IsNameOnly(named) {
  321. named = reference.WithDefaultTag(named)
  322. }
  323. ref, ok := named.(reference.NamedTagged)
  324. if !ok {
  325. return "", fmt.Errorf("invalid name: %s", named.String())
  326. }
  327. return ref.String(), nil
  328. }