backend_linux.go 10 KB

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