provider_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. package worker
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "testing"
  8. "time"
  9. . "github.com/smartystreets/goconvey/convey"
  10. )
  11. func TestRsyncProvider(t *testing.T) {
  12. Convey("Rsync Provider should work", t, func() {
  13. tmpDir, err := ioutil.TempDir("", "tunasync")
  14. defer os.RemoveAll(tmpDir)
  15. So(err, ShouldBeNil)
  16. scriptFile := filepath.Join(tmpDir, "myrsync")
  17. tmpFile := filepath.Join(tmpDir, "log_file")
  18. c := rsyncConfig{
  19. name: "tuna",
  20. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  21. rsyncCmd: scriptFile,
  22. workingDir: tmpDir,
  23. logDir: tmpDir,
  24. logFile: tmpFile,
  25. useIPv6: true,
  26. interval: 600 * time.Second,
  27. }
  28. provider, err := newRsyncProvider(c)
  29. So(err, ShouldBeNil)
  30. So(provider.Type(), ShouldEqual, provRsync)
  31. So(provider.Name(), ShouldEqual, c.name)
  32. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  33. So(provider.LogDir(), ShouldEqual, c.logDir)
  34. So(provider.LogFile(), ShouldEqual, c.logFile)
  35. So(provider.Interval(), ShouldEqual, c.interval)
  36. Convey("When entering a context (auto exit)", func() {
  37. func() {
  38. ctx := provider.EnterContext()
  39. defer provider.ExitContext()
  40. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  41. newWorkingDir := "/srv/mirror/working/tuna"
  42. ctx.Set(_WorkingDirKey, newWorkingDir)
  43. So(provider.WorkingDir(), ShouldEqual, newWorkingDir)
  44. }()
  45. Convey("After context is done", func() {
  46. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  47. })
  48. })
  49. Convey("When entering a context (manually exit)", func() {
  50. ctx := provider.EnterContext()
  51. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  52. newWorkingDir := "/srv/mirror/working/tuna"
  53. ctx.Set(_WorkingDirKey, newWorkingDir)
  54. So(provider.WorkingDir(), ShouldEqual, newWorkingDir)
  55. Convey("After context is done", func() {
  56. provider.ExitContext()
  57. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  58. })
  59. })
  60. Convey("Let's try a run", func() {
  61. scriptContent := `#!/bin/bash
  62. echo "syncing to $(pwd)"
  63. echo $RSYNC_PASSWORD $@
  64. sleep 1
  65. echo "Total file size: 1.33T bytes"
  66. echo "Done"
  67. exit 0
  68. `
  69. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  70. So(err, ShouldBeNil)
  71. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  72. expectedOutput := fmt.Sprintf(
  73. "syncing to %s\n"+
  74. "%s\n"+
  75. "Total file size: 1.33T bytes\n"+
  76. "Done\n",
  77. targetDir,
  78. fmt.Sprintf(
  79. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ "+
  80. "--delete --delete-after --delay-updates --safe-links "+
  81. "--timeout=120 --contimeout=120 -6 %s %s",
  82. provider.upstreamURL, provider.WorkingDir(),
  83. ),
  84. )
  85. err = provider.Run()
  86. So(err, ShouldBeNil)
  87. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  88. So(err, ShouldBeNil)
  89. So(string(loggedContent), ShouldEqual, expectedOutput)
  90. // fmt.Println(string(loggedContent))
  91. So(provider.DataSize(), ShouldEqual, "1.33T")
  92. })
  93. })
  94. }
  95. func TestRsyncProviderWithAuthentication(t *testing.T) {
  96. Convey("Rsync Provider with password should work", t, func() {
  97. tmpDir, err := ioutil.TempDir("", "tunasync")
  98. defer os.RemoveAll(tmpDir)
  99. So(err, ShouldBeNil)
  100. scriptFile := filepath.Join(tmpDir, "myrsync")
  101. tmpFile := filepath.Join(tmpDir, "log_file")
  102. c := rsyncConfig{
  103. name: "tuna",
  104. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  105. rsyncCmd: scriptFile,
  106. username: "tunasync",
  107. password: "tunasyncpassword",
  108. workingDir: tmpDir,
  109. extraOptions: []string{"--delete-excluded"},
  110. logDir: tmpDir,
  111. logFile: tmpFile,
  112. useIPv4: true,
  113. interval: 600 * time.Second,
  114. }
  115. provider, err := newRsyncProvider(c)
  116. So(err, ShouldBeNil)
  117. So(provider.Name(), ShouldEqual, c.name)
  118. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  119. So(provider.LogDir(), ShouldEqual, c.logDir)
  120. So(provider.LogFile(), ShouldEqual, c.logFile)
  121. So(provider.Interval(), ShouldEqual, c.interval)
  122. Convey("Let's try a run", func() {
  123. scriptContent := `#!/bin/bash
  124. echo "syncing to $(pwd)"
  125. echo $USER $RSYNC_PASSWORD $@
  126. sleep 1
  127. echo "Done"
  128. exit 0
  129. `
  130. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  131. So(err, ShouldBeNil)
  132. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  133. expectedOutput := fmt.Sprintf(
  134. "syncing to %s\n"+
  135. "%s\n"+
  136. "Done\n",
  137. targetDir,
  138. fmt.Sprintf(
  139. "%s %s -aHvh --no-o --no-g --stats --exclude .~tmp~/ "+
  140. "--delete --delete-after --delay-updates --safe-links "+
  141. "--timeout=120 --contimeout=120 -4 --delete-excluded %s %s",
  142. provider.username, provider.password, provider.upstreamURL, provider.WorkingDir(),
  143. ),
  144. )
  145. err = provider.Run()
  146. So(err, ShouldBeNil)
  147. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  148. So(err, ShouldBeNil)
  149. So(string(loggedContent), ShouldEqual, expectedOutput)
  150. // fmt.Println(string(loggedContent))
  151. })
  152. })
  153. }
  154. func TestRsyncProviderWithOverriddenOptions(t *testing.T) {
  155. Convey("Rsync Provider with overridden options should work", t, func() {
  156. tmpDir, err := ioutil.TempDir("", "tunasync")
  157. defer os.RemoveAll(tmpDir)
  158. So(err, ShouldBeNil)
  159. scriptFile := filepath.Join(tmpDir, "myrsync")
  160. tmpFile := filepath.Join(tmpDir, "log_file")
  161. c := rsyncConfig{
  162. name: "tuna",
  163. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  164. rsyncCmd: scriptFile,
  165. workingDir: tmpDir,
  166. overriddenOptions: []string{"-aHvh", "--no-o", "--no-g", "--stats"},
  167. extraOptions: []string{"--delete-excluded"},
  168. logDir: tmpDir,
  169. logFile: tmpFile,
  170. useIPv6: true,
  171. interval: 600 * time.Second,
  172. }
  173. provider, err := newRsyncProvider(c)
  174. So(err, ShouldBeNil)
  175. So(provider.Name(), ShouldEqual, c.name)
  176. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  177. So(provider.LogDir(), ShouldEqual, c.logDir)
  178. So(provider.LogFile(), ShouldEqual, c.logFile)
  179. So(provider.Interval(), ShouldEqual, c.interval)
  180. Convey("Let's try a run", func() {
  181. scriptContent := `#!/bin/bash
  182. echo "syncing to $(pwd)"
  183. echo $@
  184. sleep 1
  185. echo "Done"
  186. exit 0
  187. `
  188. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  189. So(err, ShouldBeNil)
  190. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  191. expectedOutput := fmt.Sprintf(
  192. "syncing to %s\n"+
  193. "-aHvh --no-o --no-g --stats -6 --delete-excluded %s %s\n"+
  194. "Done\n",
  195. targetDir,
  196. provider.upstreamURL,
  197. provider.WorkingDir(),
  198. )
  199. err = provider.Run()
  200. So(err, ShouldBeNil)
  201. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  202. So(err, ShouldBeNil)
  203. So(string(loggedContent), ShouldEqual, expectedOutput)
  204. // fmt.Println(string(loggedContent))
  205. })
  206. })
  207. }
  208. func TestCmdProvider(t *testing.T) {
  209. Convey("Command Provider should work", t, func(ctx C) {
  210. tmpDir, err := ioutil.TempDir("", "tunasync")
  211. defer os.RemoveAll(tmpDir)
  212. So(err, ShouldBeNil)
  213. scriptFile := filepath.Join(tmpDir, "cmd.sh")
  214. tmpFile := filepath.Join(tmpDir, "log_file")
  215. c := cmdConfig{
  216. name: "tuna-cmd",
  217. upstreamURL: "http://mirrors.tuna.moe/",
  218. command: "bash " + scriptFile,
  219. workingDir: tmpDir,
  220. logDir: tmpDir,
  221. logFile: tmpFile,
  222. interval: 600 * time.Second,
  223. env: map[string]string{
  224. "AOSP_REPO_BIN": "/usr/local/bin/repo",
  225. },
  226. }
  227. provider, err := newCmdProvider(c)
  228. So(err, ShouldBeNil)
  229. So(provider.Type(), ShouldEqual, provCommand)
  230. So(provider.Name(), ShouldEqual, c.name)
  231. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  232. So(provider.LogDir(), ShouldEqual, c.logDir)
  233. So(provider.LogFile(), ShouldEqual, c.logFile)
  234. So(provider.Interval(), ShouldEqual, c.interval)
  235. Convey("Let's try to run a simple command", func() {
  236. scriptContent := `#!/bin/bash
  237. echo $TUNASYNC_WORKING_DIR
  238. echo $TUNASYNC_MIRROR_NAME
  239. echo $TUNASYNC_UPSTREAM_URL
  240. echo $TUNASYNC_LOG_FILE
  241. echo $AOSP_REPO_BIN
  242. `
  243. expectedOutput := fmt.Sprintf(
  244. "%s\n%s\n%s\n%s\n%s\n",
  245. provider.WorkingDir(),
  246. provider.Name(),
  247. provider.upstreamURL,
  248. provider.LogFile(),
  249. "/usr/local/bin/repo",
  250. )
  251. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  252. So(err, ShouldBeNil)
  253. readedScriptContent, err := ioutil.ReadFile(scriptFile)
  254. So(err, ShouldBeNil)
  255. So(readedScriptContent, ShouldResemble, []byte(scriptContent))
  256. err = provider.Run()
  257. So(err, ShouldBeNil)
  258. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  259. So(err, ShouldBeNil)
  260. So(string(loggedContent), ShouldEqual, expectedOutput)
  261. })
  262. Convey("If a command fails", func() {
  263. scriptContent := `exit 1`
  264. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  265. So(err, ShouldBeNil)
  266. readedScriptContent, err := ioutil.ReadFile(scriptFile)
  267. So(err, ShouldBeNil)
  268. So(readedScriptContent, ShouldResemble, []byte(scriptContent))
  269. err = provider.Run()
  270. So(err, ShouldNotBeNil)
  271. })
  272. Convey("If a long job is killed", func(ctx C) {
  273. scriptContent := `#!/bin/bash
  274. sleep 5
  275. `
  276. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  277. So(err, ShouldBeNil)
  278. go func() {
  279. err = provider.Run()
  280. ctx.So(err, ShouldNotBeNil)
  281. }()
  282. time.Sleep(1 * time.Second)
  283. err = provider.Terminate()
  284. So(err, ShouldBeNil)
  285. })
  286. })
  287. Convey("Command Provider without log file should work", t, func(ctx C) {
  288. tmpDir, err := ioutil.TempDir("", "tunasync")
  289. defer os.RemoveAll(tmpDir)
  290. So(err, ShouldBeNil)
  291. c := cmdConfig{
  292. name: "run-ls",
  293. upstreamURL: "http://mirrors.tuna.moe/",
  294. command: "ls",
  295. workingDir: tmpDir,
  296. logDir: tmpDir,
  297. logFile: "/dev/null",
  298. interval: 600 * time.Second,
  299. }
  300. provider, err := newCmdProvider(c)
  301. So(err, ShouldBeNil)
  302. So(provider.IsMaster(), ShouldEqual, false)
  303. So(provider.ZFS(), ShouldBeNil)
  304. So(provider.Type(), ShouldEqual, provCommand)
  305. So(provider.Name(), ShouldEqual, c.name)
  306. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  307. So(provider.LogDir(), ShouldEqual, c.logDir)
  308. So(provider.LogFile(), ShouldEqual, c.logFile)
  309. So(provider.Interval(), ShouldEqual, c.interval)
  310. Convey("Run the command", func() {
  311. err = provider.Run()
  312. So(err, ShouldBeNil)
  313. })
  314. })
  315. Convey("Command Provider with fail-on-match regexp should work", t, func(ctx C) {
  316. tmpDir, err := ioutil.TempDir("", "tunasync")
  317. defer os.RemoveAll(tmpDir)
  318. So(err, ShouldBeNil)
  319. tmpFile := filepath.Join(tmpDir, "log_file")
  320. c := cmdConfig{
  321. name: "run-uptime",
  322. upstreamURL: "http://mirrors.tuna.moe/",
  323. command: "uptime",
  324. failOnMatch: "",
  325. workingDir: tmpDir,
  326. logDir: tmpDir,
  327. logFile: tmpFile,
  328. interval: 600 * time.Second,
  329. }
  330. Convey("when regexp matches", func() {
  331. c.failOnMatch = `[a-z]+`
  332. provider, err := newCmdProvider(c)
  333. So(err, ShouldBeNil)
  334. err = provider.Run()
  335. So(err, ShouldNotBeNil)
  336. })
  337. Convey("when regexp does not match", func() {
  338. c.failOnMatch = `load average_`
  339. provider, err := newCmdProvider(c)
  340. So(err, ShouldBeNil)
  341. err = provider.Run()
  342. So(err, ShouldBeNil)
  343. })
  344. })
  345. }
  346. func TestTwoStageRsyncProvider(t *testing.T) {
  347. Convey("TwoStageRsync Provider should work", t, func(ctx C) {
  348. tmpDir, err := ioutil.TempDir("", "tunasync")
  349. defer os.RemoveAll(tmpDir)
  350. So(err, ShouldBeNil)
  351. scriptFile := filepath.Join(tmpDir, "myrsync")
  352. tmpFile := filepath.Join(tmpDir, "log_file")
  353. c := twoStageRsyncConfig{
  354. name: "tuna-two-stage-rsync",
  355. upstreamURL: "rsync://mirrors.tuna.moe/",
  356. stage1Profile: "debian",
  357. rsyncCmd: scriptFile,
  358. workingDir: tmpDir,
  359. logDir: tmpDir,
  360. logFile: tmpFile,
  361. useIPv6: true,
  362. excludeFile: tmpFile,
  363. extraOptions: []string{"--delete-excluded", "--cache"},
  364. username: "hello",
  365. password: "world",
  366. }
  367. provider, err := newTwoStageRsyncProvider(c)
  368. So(err, ShouldBeNil)
  369. So(provider.Type(), ShouldEqual, provTwoStageRsync)
  370. So(provider.Name(), ShouldEqual, c.name)
  371. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  372. So(provider.LogDir(), ShouldEqual, c.logDir)
  373. So(provider.LogFile(), ShouldEqual, c.logFile)
  374. So(provider.Interval(), ShouldEqual, c.interval)
  375. Convey("Try a command", func(ctx C) {
  376. scriptContent := `#!/bin/bash
  377. echo "syncing to $(pwd)"
  378. echo $@
  379. sleep 1
  380. echo "Done"
  381. exit 0
  382. `
  383. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  384. So(err, ShouldBeNil)
  385. err = provider.Run()
  386. So(err, ShouldBeNil)
  387. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  388. expectedOutput := fmt.Sprintf(
  389. "syncing to %s\n"+
  390. "%s\n"+
  391. "Done\n"+
  392. "syncing to %s\n"+
  393. "%s\n"+
  394. "Done\n",
  395. targetDir,
  396. fmt.Sprintf(
  397. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ --safe-links "+
  398. "--timeout=120 --contimeout=120 --exclude dists/ -6 "+
  399. "--exclude-from %s --delete-excluded --cache %s %s",
  400. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  401. ),
  402. targetDir,
  403. fmt.Sprintf(
  404. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ "+
  405. "--delete --delete-after --delay-updates --safe-links "+
  406. "--timeout=120 --contimeout=120 -6 --exclude-from %s --delete-excluded --cache %s %s",
  407. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  408. ),
  409. )
  410. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  411. So(err, ShouldBeNil)
  412. So(string(loggedContent), ShouldEqual, expectedOutput)
  413. // fmt.Println(string(loggedContent))
  414. })
  415. Convey("Try terminating", func(ctx C) {
  416. scriptContent := `#!/bin/bash
  417. echo $@
  418. sleep 4
  419. exit 0
  420. `
  421. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  422. So(err, ShouldBeNil)
  423. go func() {
  424. err = provider.Run()
  425. ctx.So(err, ShouldNotBeNil)
  426. }()
  427. time.Sleep(1 * time.Second)
  428. err = provider.Terminate()
  429. So(err, ShouldBeNil)
  430. expectedOutput := fmt.Sprintf(
  431. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ --safe-links "+
  432. "--timeout=120 --contimeout=120 --exclude dists/ -6 "+
  433. "--exclude-from %s --delete-excluded --cache %s %s\n",
  434. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  435. )
  436. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  437. So(err, ShouldBeNil)
  438. So(string(loggedContent), ShouldEqual, expectedOutput)
  439. // fmt.Println(string(loggedContent))
  440. })
  441. })
  442. }