manager_linux.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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) error {
  20. if p.PluginObj.Active {
  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. fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
  82. if err != nil {
  83. return nil, err
  84. }
  85. if fi.IsDir() {
  86. if err := os.MkdirAll(m.Source, 0700); err != nil {
  87. return nil, err
  88. }
  89. }
  90. }
  91. s.Mounts = append(s.Mounts, m)
  92. }
  93. envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
  94. envs[0] = "PATH=" + system.DefaultPathEnv
  95. envs = append(envs, p.PluginObj.Config.Env...)
  96. args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
  97. cwd := p.PluginObj.Manifest.Workdir
  98. if len(cwd) == 0 {
  99. cwd = "/"
  100. }
  101. s.Process = specs.Process{
  102. Terminal: false,
  103. Args: args,
  104. Cwd: cwd,
  105. Env: envs,
  106. }
  107. return &s, nil
  108. }
  109. func (pm *Manager) disable(p *plugin) error {
  110. if !p.PluginObj.Active {
  111. return fmt.Errorf("plugin %s is already disabled", p.Name())
  112. }
  113. if err := p.restartManager.Cancel(); err != nil {
  114. logrus.Error(err)
  115. }
  116. if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
  117. logrus.Error(err)
  118. }
  119. os.RemoveAll(p.runtimeSourcePath)
  120. pm.Lock() // fixme: lock single record
  121. defer pm.Unlock()
  122. p.PluginObj.Active = false
  123. pm.save()
  124. return nil
  125. }
  126. // Shutdown stops all plugins and called during daemon shutdown.
  127. func (pm *Manager) Shutdown() {
  128. pm.RLock()
  129. defer pm.RUnlock()
  130. pm.shutdown = true
  131. for _, p := range pm.plugins {
  132. if pm.liveRestore && p.PluginObj.Active {
  133. logrus.Debug("Plugin active when liveRestore is set, skipping shutdown")
  134. continue
  135. }
  136. if p.restartManager != nil {
  137. if err := p.restartManager.Cancel(); err != nil {
  138. logrus.Error(err)
  139. }
  140. }
  141. if pm.containerdClient != nil && p.PluginObj.Active {
  142. p.exitChan = make(chan bool)
  143. err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGTERM))
  144. if err != nil {
  145. logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
  146. } else {
  147. select {
  148. case <-p.exitChan:
  149. logrus.Debug("Clean shutdown of plugin")
  150. case <-time.After(time.Second * 10):
  151. logrus.Debug("Force shutdown plugin")
  152. if err := pm.containerdClient.Signal(p.PluginObj.ID, int(syscall.SIGKILL)); err != nil {
  153. logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
  154. }
  155. }
  156. }
  157. close(p.exitChan)
  158. pm.Lock()
  159. p.PluginObj.Active = false
  160. pm.save()
  161. pm.Unlock()
  162. }
  163. if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
  164. logrus.Errorf("Remove plugin runtime failed with error: %v", err)
  165. }
  166. }
  167. }