manager_linux.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // +build linux,experimental
  2. package plugin
  3. import (
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "syscall"
  8. "time"
  9. "github.com/Sirupsen/logrus"
  10. "github.com/docker/docker/libcontainerd"
  11. "github.com/docker/docker/oci"
  12. "github.com/docker/docker/pkg/plugins"
  13. "github.com/docker/docker/pkg/system"
  14. "github.com/docker/docker/restartmanager"
  15. "github.com/docker/engine-api/types"
  16. "github.com/docker/engine-api/types/container"
  17. "github.com/opencontainers/specs/specs-go"
  18. )
  19. func (pm *Manager) enable(p *plugin, force bool) error {
  20. if p.PluginObj.Active && !force {
  21. return fmt.Errorf("plugin %s is already enabled", p.Name())
  22. }
  23. spec, err := pm.initSpec(p)
  24. if err != nil {
  25. return err
  26. }
  27. p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
  28. if err := pm.containerdClient.Create(p.PluginObj.ID, libcontainerd.Spec(*spec), libcontainerd.WithRestartManager(p.restartManager)); err != nil { // POC-only
  29. if err := p.restartManager.Cancel(); err != nil {
  30. logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
  31. }
  32. return err
  33. }
  34. socket := p.PluginObj.Manifest.Interface.Socket
  35. p.client, err = plugins.NewClient("unix://"+filepath.Join(p.runtimeSourcePath, socket), nil)
  36. if err != nil {
  37. if err := p.restartManager.Cancel(); err != nil {
  38. logrus.Errorf("enable: restartManager.Cancel failed due to %v", err)
  39. }
  40. return err
  41. }
  42. pm.Lock() // fixme: lock single record
  43. p.PluginObj.Active = true
  44. pm.save()
  45. pm.Unlock()
  46. for _, typ := range p.PluginObj.Manifest.Interface.Types {
  47. if handler := pm.handlers[typ.String()]; handler != nil {
  48. handler(p.Name(), p.Client())
  49. }
  50. }
  51. return nil
  52. }
  53. func (pm *Manager) restore(p *plugin) error {
  54. p.restartManager = restartmanager.New(container.RestartPolicy{Name: "always"}, 0)
  55. return pm.containerdClient.Restore(p.PluginObj.ID, libcontainerd.WithRestartManager(p.restartManager))
  56. }
  57. func (pm *Manager) initSpec(p *plugin) (*specs.Spec, error) {
  58. s := oci.DefaultSpec()
  59. rootfs := filepath.Join(pm.libRoot, p.PluginObj.ID, "rootfs")
  60. s.Root = specs.Root{
  61. Path: rootfs,
  62. Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
  63. }
  64. mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
  65. Source: &p.runtimeSourcePath,
  66. Destination: defaultPluginRuntimeDestination,
  67. Type: "bind",
  68. Options: []string{"rbind", "rshared"},
  69. })
  70. for _, mount := range mounts {
  71. m := specs.Mount{
  72. Destination: mount.Destination,
  73. Type: mount.Type,
  74. Options: mount.Options,
  75. }
  76. // TODO: if nil, then it's required and user didn't set it
  77. if mount.Source != nil {
  78. m.Source = *mount.Source
  79. }
  80. if m.Source != "" && m.Type == "bind" {
  81. /* Debugging issue #25511: Volumes and other content created under the
  82. bind mount should be recursively propagated. rshared, not shared.
  83. This could be the reason for EBUSY during removal. Override options
  84. with rbind, rshared and see if CI errors are fixed. */
  85. m.Options = []string{"rbind", "rshared"}
  86. fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
  87. if err != nil {
  88. return nil, err
  89. }
  90. if fi.IsDir() {
  91. if err := os.MkdirAll(m.Source, 0700); err != nil {
  92. return nil, err
  93. }
  94. }
  95. }
  96. s.Mounts = append(s.Mounts, m)
  97. }
  98. envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
  99. envs[0] = "PATH=" + system.DefaultPathEnv
  100. envs = append(envs, p.PluginObj.Config.Env...)
  101. args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
  102. cwd := p.PluginObj.Manifest.Workdir
  103. if len(cwd) == 0 {
  104. cwd = "/"
  105. }
  106. s.Process = specs.Process{
  107. Terminal: false,
  108. Args: args,
  109. Cwd: cwd,
  110. Env: envs,
  111. }
  112. return &s, nil
  113. }
  114. func (pm *Manager) disable(p *plugin) error {
  115. if !p.PluginObj.Active {
  116. return fmt.Errorf("plugin %s is already disabled", p.Name())
  117. }
  118. if err := p.restartManager.Cancel(); err != nil {
  119. logrus.Error(err)
  120. }
  121. if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
  122. logrus.Error(err)
  123. }
  124. os.RemoveAll(p.runtimeSourcePath)
  125. pm.Lock() // fixme: lock single record
  126. defer pm.Unlock()
  127. p.PluginObj.Active = false
  128. pm.save()
  129. return nil
  130. }
  131. // Shutdown stops all plugins and called during daemon shutdown.
  132. func (pm *Manager) Shutdown() {
  133. pm.RLock()
  134. defer pm.RUnlock()
  135. pm.shutdown = true
  136. for _, p := range pm.plugins {
  137. if pm.liveRestore && p.PluginObj.Active {
  138. logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
  139. continue
  140. }
  141. if p.restartManager != nil {
  142. if err := p.restartManager.Cancel(); err != nil {
  143. logrus.Error(err)
  144. }
  145. }
  146. if pm.containerdClient != nil && p.PluginObj.Active {
  147. p.exitChan = make(chan bool)
  148. err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
  149. if err != nil {
  150. logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
  151. } else {
  152. select {
  153. case <-p.exitChan:
  154. logrus.Debug("Clean shutdown of plugin")
  155. case <-time.After(time.Second * 10):
  156. logrus.Debug("Force shutdown plugin")
  157. if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
  158. logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
  159. }
  160. }
  161. }
  162. close(p.exitChan)
  163. pm.Lock()
  164. p.PluginObj.Active = false
  165. pm.save()
  166. pm.Unlock()
  167. }
  168. if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
  169. logrus.Errorf("Remove plugin runtime failed with error: %v", err)
  170. }
  171. }
  172. }