cgroup_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package worker
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "path/filepath"
  6. "strconv"
  7. "strings"
  8. "testing"
  9. "time"
  10. "errors"
  11. "syscall"
  12. cgv1 "github.com/containerd/cgroups"
  13. cgv2 "github.com/containerd/cgroups/v2"
  14. units "github.com/docker/go-units"
  15. "github.com/moby/moby/pkg/reexec"
  16. . "github.com/smartystreets/goconvey/convey"
  17. )
  18. func init() {
  19. reexec.Init()
  20. }
  21. func TestCgroup(t *testing.T) {
  22. var cgcf *cgroupConfig
  23. Convey("init cgroup", t, func(ctx C){
  24. _, useCurrentCgroup := os.LookupEnv("USECURCGROUP")
  25. cgcf = &cgroupConfig{BasePath: "/sys/fs/cgroup", Group: "tunasync", Subsystem: "cpu"}
  26. if useCurrentCgroup {
  27. cgcf.Group = ""
  28. }
  29. err := initCgroup(cgcf)
  30. So(err, ShouldBeNil)
  31. if cgcf.isUnified {
  32. So(cgcf.cgMgrV2, ShouldNotBeNil)
  33. } else {
  34. So(cgcf.cgMgrV1, ShouldNotBeNil)
  35. }
  36. Convey("Cgroup Should Work", func(ctx C) {
  37. tmpDir, err := ioutil.TempDir("", "tunasync")
  38. defer os.RemoveAll(tmpDir)
  39. So(err, ShouldBeNil)
  40. cmdScript := filepath.Join(tmpDir, "cmd.sh")
  41. daemonScript := filepath.Join(tmpDir, "daemon.sh")
  42. tmpFile := filepath.Join(tmpDir, "log_file")
  43. bgPidfile := filepath.Join(tmpDir, "bg.pid")
  44. c := cmdConfig{
  45. name: "tuna-cgroup",
  46. upstreamURL: "http://mirrors.tuna.moe/",
  47. command: cmdScript + " " + daemonScript,
  48. workingDir: tmpDir,
  49. logDir: tmpDir,
  50. logFile: tmpFile,
  51. interval: 600 * time.Second,
  52. env: map[string]string{
  53. "BG_PIDFILE": bgPidfile,
  54. },
  55. }
  56. cmdScriptContent := `#!/bin/bash
  57. redirect-std() {
  58. [[ -t 0 ]] && exec </dev/null
  59. [[ -t 1 ]] && exec >/dev/null
  60. [[ -t 2 ]] && exec 2>/dev/null
  61. }
  62. # close all non-std* fds
  63. close-fds() {
  64. eval exec {3..255}\>\&-
  65. }
  66. # full daemonization of external command with setsid
  67. daemonize() {
  68. (
  69. redirect-std
  70. cd /
  71. close-fds
  72. exec setsid "$@"
  73. ) &
  74. }
  75. echo $$
  76. daemonize $@
  77. sleep 5
  78. `
  79. daemonScriptContent := `#!/bin/bash
  80. echo $$ > $BG_PIDFILE
  81. sleep 30
  82. `
  83. err = ioutil.WriteFile(cmdScript, []byte(cmdScriptContent), 0755)
  84. So(err, ShouldBeNil)
  85. err = ioutil.WriteFile(daemonScript, []byte(daemonScriptContent), 0755)
  86. So(err, ShouldBeNil)
  87. provider, err := newCmdProvider(c)
  88. So(err, ShouldBeNil)
  89. cg := newCgroupHook(provider, *cgcf, 0)
  90. provider.AddHook(cg)
  91. err = cg.preExec()
  92. So(err, ShouldBeNil)
  93. go func() {
  94. err := provider.Run(make(chan empty, 1))
  95. ctx.So(err, ShouldNotBeNil)
  96. }()
  97. time.Sleep(1 * time.Second)
  98. // Deamon should be started
  99. daemonPidBytes, err := ioutil.ReadFile(bgPidfile)
  100. So(err, ShouldBeNil)
  101. daemonPid := strings.Trim(string(daemonPidBytes), " \n")
  102. logger.Debug("daemon pid: %s", daemonPid)
  103. procDir := filepath.Join("/proc", daemonPid)
  104. _, err = os.Stat(procDir)
  105. So(err, ShouldBeNil)
  106. err = provider.Terminate()
  107. So(err, ShouldBeNil)
  108. // Deamon won't be killed
  109. _, err = os.Stat(procDir)
  110. So(err, ShouldBeNil)
  111. // Deamon can be killed by cgroup killer
  112. cg.postExec()
  113. _, err = os.Stat(procDir)
  114. So(os.IsNotExist(err), ShouldBeTrue)
  115. })
  116. Convey("Rsync Memory Should Be Limited", func() {
  117. tmpDir, err := ioutil.TempDir("", "tunasync")
  118. defer os.RemoveAll(tmpDir)
  119. So(err, ShouldBeNil)
  120. scriptFile := filepath.Join(tmpDir, "myrsync")
  121. tmpFile := filepath.Join(tmpDir, "log_file")
  122. c := rsyncConfig{
  123. name: "tuna-cgroup",
  124. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  125. rsyncCmd: scriptFile,
  126. workingDir: tmpDir,
  127. logDir: tmpDir,
  128. logFile: tmpFile,
  129. useIPv6: true,
  130. interval: 600 * time.Second,
  131. }
  132. provider, err := newRsyncProvider(c)
  133. So(err, ShouldBeNil)
  134. cg := newCgroupHook(provider, *cgcf, 512 * units.MiB)
  135. provider.AddHook(cg)
  136. err = cg.preExec()
  137. So(err, ShouldBeNil)
  138. if cgcf.isUnified {
  139. cgpath := filepath.Join(cgcf.BasePath, cgcf.Group, provider.Name())
  140. if useCurrentCgroup {
  141. group, err := cgv2.NestedGroupPath(filepath.Join("..", provider.Name()))
  142. So(err, ShouldBeNil)
  143. cgpath = filepath.Join(cgcf.BasePath, group)
  144. }
  145. memoLimit, err := ioutil.ReadFile(filepath.Join(cgpath, "memory.max"))
  146. So(err, ShouldBeNil)
  147. So(strings.Trim(string(memoLimit), "\n"), ShouldEqual, strconv.Itoa(512*1024*1024))
  148. } else {
  149. for _, subsys := range(cg.cgMgrV1.Subsystems()) {
  150. if subsys.Name() == cgv1.Memory {
  151. cgpath := filepath.Join(cgcf.Group, provider.Name())
  152. if useCurrentCgroup {
  153. p, err := cgv1.NestedPath(filepath.Join("..", provider.Name()))(cgv1.Memory)
  154. So(err, ShouldBeNil)
  155. cgpath = p
  156. }
  157. memoLimit, err := ioutil.ReadFile(filepath.Join(cgcf.BasePath, "memory", cgpath, "memory.limit_in_bytes"))
  158. So(err, ShouldBeNil)
  159. So(strings.Trim(string(memoLimit), "\n"), ShouldEqual, strconv.Itoa(512*1024*1024))
  160. }
  161. }
  162. }
  163. cg.postExec()
  164. So(cg.cgMgrV1, ShouldBeNil)
  165. })
  166. Reset(func() {
  167. if cgcf.isUnified {
  168. if cgcf.Group == "" {
  169. wkrg, err := cgv2.NestedGroupPath("");
  170. So(err, ShouldBeNil)
  171. wkrMgr, err := cgv2.LoadManager("/sys/fs/cgroup", wkrg);
  172. allCtrls, err := wkrMgr.Controllers()
  173. So(err, ShouldBeNil)
  174. err = wkrMgr.ToggleControllers(allCtrls, cgv2.Disable)
  175. So(err, ShouldBeNil)
  176. origMgr := cgcf.cgMgrV2
  177. for {
  178. logger.Debugf("Restoring pids")
  179. procs, err := wkrMgr.Procs(false)
  180. So(err, ShouldBeNil)
  181. if len(procs) == 0 {
  182. break
  183. }
  184. for _, p := range(procs) {
  185. if err := origMgr.AddProc(p); err != nil{
  186. if errors.Is(err, syscall.ESRCH) {
  187. logger.Debugf("Write pid %d to sub group failed: process vanished, ignoring")
  188. } else {
  189. So(err, ShouldBeNil)
  190. }
  191. }
  192. }
  193. }
  194. err = wkrMgr.Delete()
  195. So(err, ShouldBeNil)
  196. }
  197. } else {
  198. if cgcf.Group == "" {
  199. pather := (func(p cgv1.Path) (cgv1.Path){
  200. return func(subsys cgv1.Name) (string, error){
  201. path, err := p(subsys);
  202. if err != nil {
  203. return "", err
  204. }
  205. if path == "/" {
  206. return "", cgv1.ErrControllerNotActive
  207. }
  208. return path, err
  209. }
  210. })(cgv1.NestedPath(""))
  211. wkrMgr, err := cgv1.Load(cgv1.V1, pather, func(cfg *cgv1.InitConfig) error{
  212. cfg.InitCheck = cgv1.AllowAny
  213. return nil
  214. })
  215. So(err, ShouldBeNil)
  216. origMgr := cgcf.cgMgrV1
  217. for _, subsys := range(wkrMgr.Subsystems()){
  218. for {
  219. procs, err := wkrMgr.Processes(subsys.Name(), false)
  220. So(err, ShouldBeNil)
  221. if len(procs) == 0 {
  222. break
  223. }
  224. for _, proc := range(procs) {
  225. if err := origMgr.Add(proc); err != nil {
  226. if errors.Is(err, syscall.ESRCH) {
  227. logger.Debugf("Write pid %d to sub group failed: process vanished, ignoring")
  228. } else {
  229. So(err, ShouldBeNil)
  230. }
  231. }
  232. }
  233. }
  234. }
  235. err = wkrMgr.Delete()
  236. So(err, ShouldBeNil)
  237. }
  238. }
  239. })
  240. })
  241. }