manager_linux.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // +build linux
  2. package plugin
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "syscall"
  9. "time"
  10. "github.com/Sirupsen/logrus"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/daemon/initlayer"
  13. "github.com/docker/docker/libcontainerd"
  14. "github.com/docker/docker/pkg/mount"
  15. "github.com/docker/docker/pkg/plugins"
  16. "github.com/docker/docker/pkg/stringid"
  17. "github.com/docker/docker/plugin/v2"
  18. "github.com/opencontainers/go-digest"
  19. specs "github.com/opencontainers/runtime-spec/specs-go"
  20. "github.com/pkg/errors"
  21. )
  22. func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
  23. p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
  24. if p.IsEnabled() && !force {
  25. return fmt.Errorf("plugin %s is already enabled", p.Name())
  26. }
  27. spec, err := p.InitSpec(pm.config.ExecRoot)
  28. if err != nil {
  29. return err
  30. }
  31. c.restart = true
  32. c.exitChan = make(chan bool)
  33. pm.mu.Lock()
  34. pm.cMap[p] = c
  35. pm.mu.Unlock()
  36. if p.PropagatedMount != "" {
  37. if err := mount.MakeRShared(p.PropagatedMount); err != nil {
  38. return errors.WithStack(err)
  39. }
  40. }
  41. if err := initlayer.Setup(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName), 0, 0); err != nil {
  42. return errors.WithStack(err)
  43. }
  44. if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
  45. if p.PropagatedMount != "" {
  46. if err := mount.Unmount(p.PropagatedMount); err != nil {
  47. logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
  48. }
  49. }
  50. return errors.WithStack(err)
  51. }
  52. return pm.pluginPostStart(p, c)
  53. }
  54. func (pm *Manager) pluginPostStart(p *v2.Plugin, c *controller) error {
  55. client, err := plugins.NewClientWithTimeout("unix://"+filepath.Join(pm.config.ExecRoot, p.GetID(), p.GetSocket()), nil, c.timeoutInSecs)
  56. if err != nil {
  57. c.restart = false
  58. shutdownPlugin(p, c, pm.containerdClient)
  59. return errors.WithStack(err)
  60. }
  61. p.SetPClient(client)
  62. pm.config.Store.SetState(p, true)
  63. pm.config.Store.CallHandler(p)
  64. return pm.save(p)
  65. }
  66. func (pm *Manager) restore(p *v2.Plugin) error {
  67. if err := pm.containerdClient.Restore(p.GetID(), attachToLog(p.GetID())); err != nil {
  68. return err
  69. }
  70. if pm.config.LiveRestoreEnabled {
  71. c := &controller{}
  72. if pids, _ := pm.containerdClient.GetPidsForContainer(p.GetID()); len(pids) == 0 {
  73. // plugin is not running, so follow normal startup procedure
  74. return pm.enable(p, c, true)
  75. }
  76. c.exitChan = make(chan bool)
  77. c.restart = true
  78. pm.mu.Lock()
  79. pm.cMap[p] = c
  80. pm.mu.Unlock()
  81. return pm.pluginPostStart(p, c)
  82. }
  83. return nil
  84. }
  85. func shutdownPlugin(p *v2.Plugin, c *controller, containerdClient libcontainerd.Client) {
  86. pluginID := p.GetID()
  87. err := containerdClient.Signal(pluginID, int(syscall.SIGTERM))
  88. if err != nil {
  89. logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
  90. } else {
  91. select {
  92. case <-c.exitChan:
  93. logrus.Debug("Clean shutdown of plugin")
  94. case <-time.After(time.Second * 10):
  95. logrus.Debug("Force shutdown plugin")
  96. if err := containerdClient.Signal(pluginID, int(syscall.SIGKILL)); err != nil {
  97. logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
  98. }
  99. }
  100. }
  101. }
  102. func (pm *Manager) disable(p *v2.Plugin, c *controller) error {
  103. if !p.IsEnabled() {
  104. return fmt.Errorf("plugin %s is already disabled", p.Name())
  105. }
  106. c.restart = false
  107. shutdownPlugin(p, c, pm.containerdClient)
  108. pm.config.Store.SetState(p, false)
  109. return pm.save(p)
  110. }
  111. // Shutdown stops all plugins and called during daemon shutdown.
  112. func (pm *Manager) Shutdown() {
  113. plugins := pm.config.Store.GetAll()
  114. for _, p := range plugins {
  115. pm.mu.RLock()
  116. c := pm.cMap[p]
  117. pm.mu.RUnlock()
  118. if pm.config.LiveRestoreEnabled && p.IsEnabled() {
  119. logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
  120. continue
  121. }
  122. if pm.containerdClient != nil && p.IsEnabled() {
  123. c.restart = false
  124. shutdownPlugin(p, c, pm.containerdClient)
  125. }
  126. }
  127. }
  128. // createPlugin creates a new plugin. take lock before calling.
  129. func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges) (p *v2.Plugin, err error) {
  130. if err := pm.config.Store.validateName(name); err != nil { // todo: this check is wrong. remove store
  131. return nil, err
  132. }
  133. configRC, err := pm.blobStore.Get(configDigest)
  134. if err != nil {
  135. return nil, err
  136. }
  137. defer configRC.Close()
  138. var config types.PluginConfig
  139. dec := json.NewDecoder(configRC)
  140. if err := dec.Decode(&config); err != nil {
  141. return nil, errors.Wrapf(err, "failed to parse config")
  142. }
  143. if dec.More() {
  144. return nil, errors.New("invalid config json")
  145. }
  146. requiredPrivileges, err := computePrivileges(config)
  147. if err != nil {
  148. return nil, err
  149. }
  150. if privileges != nil {
  151. if err := validatePrivileges(requiredPrivileges, *privileges); err != nil {
  152. return nil, err
  153. }
  154. }
  155. p = &v2.Plugin{
  156. PluginObj: types.Plugin{
  157. Name: name,
  158. ID: stringid.GenerateRandomID(),
  159. Config: config,
  160. },
  161. Config: configDigest,
  162. Blobsums: blobsums,
  163. }
  164. p.InitEmptySettings()
  165. pdir := filepath.Join(pm.config.Root, p.PluginObj.ID)
  166. if err := os.MkdirAll(pdir, 0700); err != nil {
  167. return nil, errors.Wrapf(err, "failed to mkdir %v", pdir)
  168. }
  169. defer func() {
  170. if err != nil {
  171. os.RemoveAll(pdir)
  172. }
  173. }()
  174. if err := os.Rename(rootFSDir, filepath.Join(pdir, rootFSFileName)); err != nil {
  175. return nil, errors.Wrap(err, "failed to rename rootfs")
  176. }
  177. if err := pm.save(p); err != nil {
  178. return nil, err
  179. }
  180. pm.config.Store.Add(p) // todo: remove
  181. return p, nil
  182. }