provider_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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 -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. proxyAddr := "127.0.0.1:1233"
  103. c := rsyncConfig{
  104. name: "tuna",
  105. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  106. rsyncCmd: scriptFile,
  107. username: "tunasync",
  108. password: "tunasyncpassword",
  109. workingDir: tmpDir,
  110. extraOptions: []string{"--delete-excluded"},
  111. rsyncEnv: map[string]string{"RSYNC_PROXY": proxyAddr},
  112. logDir: tmpDir,
  113. logFile: tmpFile,
  114. useIPv4: true,
  115. interval: 600 * time.Second,
  116. }
  117. provider, err := newRsyncProvider(c)
  118. So(err, ShouldBeNil)
  119. So(provider.Name(), ShouldEqual, c.name)
  120. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  121. So(provider.LogDir(), ShouldEqual, c.logDir)
  122. So(provider.LogFile(), ShouldEqual, c.logFile)
  123. So(provider.Interval(), ShouldEqual, c.interval)
  124. Convey("Let's try a run", func() {
  125. scriptContent := `#!/bin/bash
  126. echo "syncing to $(pwd)"
  127. echo $USER $RSYNC_PASSWORD $RSYNC_PROXY $@
  128. sleep 1
  129. echo "Done"
  130. exit 0
  131. `
  132. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  133. So(err, ShouldBeNil)
  134. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  135. expectedOutput := fmt.Sprintf(
  136. "syncing to %s\n"+
  137. "%s\n"+
  138. "Done\n",
  139. targetDir,
  140. fmt.Sprintf(
  141. "%s %s %s -aHvh --no-o --no-g --stats --exclude .~tmp~/ "+
  142. "--delete --delete-after --delay-updates --safe-links "+
  143. "--timeout=120 -4 --delete-excluded %s %s",
  144. provider.username, provider.password, proxyAddr,
  145. provider.upstreamURL, provider.WorkingDir(),
  146. ),
  147. )
  148. err = provider.Run()
  149. So(err, ShouldBeNil)
  150. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  151. So(err, ShouldBeNil)
  152. So(string(loggedContent), ShouldEqual, expectedOutput)
  153. // fmt.Println(string(loggedContent))
  154. })
  155. })
  156. }
  157. func TestRsyncProviderWithOverriddenOptions(t *testing.T) {
  158. Convey("Rsync Provider with overridden options should work", t, func() {
  159. tmpDir, err := ioutil.TempDir("", "tunasync")
  160. defer os.RemoveAll(tmpDir)
  161. So(err, ShouldBeNil)
  162. scriptFile := filepath.Join(tmpDir, "myrsync")
  163. tmpFile := filepath.Join(tmpDir, "log_file")
  164. c := rsyncConfig{
  165. name: "tuna",
  166. upstreamURL: "rsync://rsync.tuna.moe/tuna/",
  167. rsyncCmd: scriptFile,
  168. workingDir: tmpDir,
  169. overriddenOptions: []string{"-aHvh", "--no-o", "--no-g", "--stats"},
  170. extraOptions: []string{"--delete-excluded"},
  171. logDir: tmpDir,
  172. logFile: tmpFile,
  173. useIPv6: true,
  174. interval: 600 * time.Second,
  175. }
  176. provider, err := newRsyncProvider(c)
  177. So(err, ShouldBeNil)
  178. So(provider.Name(), ShouldEqual, c.name)
  179. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  180. So(provider.LogDir(), ShouldEqual, c.logDir)
  181. So(provider.LogFile(), ShouldEqual, c.logFile)
  182. So(provider.Interval(), ShouldEqual, c.interval)
  183. Convey("Let's try a run", func() {
  184. scriptContent := `#!/bin/bash
  185. echo "syncing to $(pwd)"
  186. echo $@
  187. sleep 1
  188. echo "Done"
  189. exit 0
  190. `
  191. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  192. So(err, ShouldBeNil)
  193. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  194. expectedOutput := fmt.Sprintf(
  195. "syncing to %s\n"+
  196. "-aHvh --no-o --no-g --stats -6 --delete-excluded %s %s\n"+
  197. "Done\n",
  198. targetDir,
  199. provider.upstreamURL,
  200. provider.WorkingDir(),
  201. )
  202. err = provider.Run()
  203. So(err, ShouldBeNil)
  204. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  205. So(err, ShouldBeNil)
  206. So(string(loggedContent), ShouldEqual, expectedOutput)
  207. // fmt.Println(string(loggedContent))
  208. })
  209. })
  210. }
  211. func TestCmdProvider(t *testing.T) {
  212. Convey("Command Provider should work", t, func(ctx C) {
  213. tmpDir, err := ioutil.TempDir("", "tunasync")
  214. defer os.RemoveAll(tmpDir)
  215. So(err, ShouldBeNil)
  216. scriptFile := filepath.Join(tmpDir, "cmd.sh")
  217. tmpFile := filepath.Join(tmpDir, "log_file")
  218. c := cmdConfig{
  219. name: "tuna-cmd",
  220. upstreamURL: "http://mirrors.tuna.moe/",
  221. command: "bash " + scriptFile,
  222. workingDir: tmpDir,
  223. logDir: tmpDir,
  224. logFile: tmpFile,
  225. interval: 600 * time.Second,
  226. env: map[string]string{
  227. "AOSP_REPO_BIN": "/usr/local/bin/repo",
  228. },
  229. }
  230. provider, err := newCmdProvider(c)
  231. So(err, ShouldBeNil)
  232. So(provider.Type(), ShouldEqual, provCommand)
  233. So(provider.Name(), ShouldEqual, c.name)
  234. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  235. So(provider.LogDir(), ShouldEqual, c.logDir)
  236. So(provider.LogFile(), ShouldEqual, c.logFile)
  237. So(provider.Interval(), ShouldEqual, c.interval)
  238. Convey("Let's try to run a simple command", func() {
  239. scriptContent := `#!/bin/bash
  240. echo $TUNASYNC_WORKING_DIR
  241. echo $TUNASYNC_MIRROR_NAME
  242. echo $TUNASYNC_UPSTREAM_URL
  243. echo $TUNASYNC_LOG_FILE
  244. echo $AOSP_REPO_BIN
  245. `
  246. expectedOutput := fmt.Sprintf(
  247. "%s\n%s\n%s\n%s\n%s\n",
  248. provider.WorkingDir(),
  249. provider.Name(),
  250. provider.upstreamURL,
  251. provider.LogFile(),
  252. "/usr/local/bin/repo",
  253. )
  254. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  255. So(err, ShouldBeNil)
  256. readedScriptContent, err := ioutil.ReadFile(scriptFile)
  257. So(err, ShouldBeNil)
  258. So(readedScriptContent, ShouldResemble, []byte(scriptContent))
  259. err = provider.Run()
  260. So(err, ShouldBeNil)
  261. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  262. So(err, ShouldBeNil)
  263. So(string(loggedContent), ShouldEqual, expectedOutput)
  264. })
  265. Convey("If a command fails", func() {
  266. scriptContent := `exit 1`
  267. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  268. So(err, ShouldBeNil)
  269. readedScriptContent, err := ioutil.ReadFile(scriptFile)
  270. So(err, ShouldBeNil)
  271. So(readedScriptContent, ShouldResemble, []byte(scriptContent))
  272. err = provider.Run()
  273. So(err, ShouldNotBeNil)
  274. })
  275. Convey("If a long job is killed", func(ctx C) {
  276. scriptContent := `#!/bin/bash
  277. sleep 5
  278. `
  279. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  280. So(err, ShouldBeNil)
  281. go func() {
  282. err = provider.Run()
  283. ctx.So(err, ShouldNotBeNil)
  284. }()
  285. time.Sleep(1 * time.Second)
  286. err = provider.Terminate()
  287. So(err, ShouldBeNil)
  288. })
  289. })
  290. Convey("Command Provider without log file should work", t, func(ctx C) {
  291. tmpDir, err := ioutil.TempDir("", "tunasync")
  292. defer os.RemoveAll(tmpDir)
  293. So(err, ShouldBeNil)
  294. c := cmdConfig{
  295. name: "run-ls",
  296. upstreamURL: "http://mirrors.tuna.moe/",
  297. command: "ls",
  298. workingDir: tmpDir,
  299. logDir: tmpDir,
  300. logFile: "/dev/null",
  301. interval: 600 * time.Second,
  302. }
  303. provider, err := newCmdProvider(c)
  304. So(err, ShouldBeNil)
  305. So(provider.IsMaster(), ShouldEqual, false)
  306. So(provider.ZFS(), ShouldBeNil)
  307. So(provider.Type(), ShouldEqual, provCommand)
  308. So(provider.Name(), ShouldEqual, c.name)
  309. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  310. So(provider.LogDir(), ShouldEqual, c.logDir)
  311. So(provider.LogFile(), ShouldEqual, c.logFile)
  312. So(provider.Interval(), ShouldEqual, c.interval)
  313. Convey("Run the command", func() {
  314. err = provider.Run()
  315. So(err, ShouldBeNil)
  316. })
  317. })
  318. Convey("Command Provider with fail-on-match regexp should work", t, func(ctx C) {
  319. tmpDir, err := ioutil.TempDir("", "tunasync")
  320. defer os.RemoveAll(tmpDir)
  321. So(err, ShouldBeNil)
  322. tmpFile := filepath.Join(tmpDir, "log_file")
  323. c := cmdConfig{
  324. name: "run-uptime",
  325. upstreamURL: "http://mirrors.tuna.moe/",
  326. command: "uptime",
  327. failOnMatch: "",
  328. workingDir: tmpDir,
  329. logDir: tmpDir,
  330. logFile: tmpFile,
  331. interval: 600 * time.Second,
  332. }
  333. Convey("when regexp matches", func() {
  334. c.failOnMatch = `[a-z]+`
  335. provider, err := newCmdProvider(c)
  336. So(err, ShouldBeNil)
  337. err = provider.Run()
  338. So(err, ShouldNotBeNil)
  339. })
  340. Convey("when regexp does not match", func() {
  341. c.failOnMatch = `load average_`
  342. provider, err := newCmdProvider(c)
  343. So(err, ShouldBeNil)
  344. err = provider.Run()
  345. So(err, ShouldBeNil)
  346. })
  347. })
  348. }
  349. func TestTwoStageRsyncProvider(t *testing.T) {
  350. Convey("TwoStageRsync Provider should work", t, func(ctx C) {
  351. tmpDir, err := ioutil.TempDir("", "tunasync")
  352. defer os.RemoveAll(tmpDir)
  353. So(err, ShouldBeNil)
  354. scriptFile := filepath.Join(tmpDir, "myrsync")
  355. tmpFile := filepath.Join(tmpDir, "log_file")
  356. c := twoStageRsyncConfig{
  357. name: "tuna-two-stage-rsync",
  358. upstreamURL: "rsync://mirrors.tuna.moe/",
  359. stage1Profile: "debian",
  360. rsyncCmd: scriptFile,
  361. workingDir: tmpDir,
  362. logDir: tmpDir,
  363. logFile: tmpFile,
  364. useIPv6: true,
  365. excludeFile: tmpFile,
  366. extraOptions: []string{"--delete-excluded", "--cache"},
  367. username: "hello",
  368. password: "world",
  369. }
  370. provider, err := newTwoStageRsyncProvider(c)
  371. So(err, ShouldBeNil)
  372. So(provider.Type(), ShouldEqual, provTwoStageRsync)
  373. So(provider.Name(), ShouldEqual, c.name)
  374. So(provider.WorkingDir(), ShouldEqual, c.workingDir)
  375. So(provider.LogDir(), ShouldEqual, c.logDir)
  376. So(provider.LogFile(), ShouldEqual, c.logFile)
  377. So(provider.Interval(), ShouldEqual, c.interval)
  378. Convey("Try a command", func(ctx C) {
  379. scriptContent := `#!/bin/bash
  380. echo "syncing to $(pwd)"
  381. echo $@
  382. sleep 1
  383. echo "Done"
  384. exit 0
  385. `
  386. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  387. So(err, ShouldBeNil)
  388. err = provider.Run()
  389. So(err, ShouldBeNil)
  390. targetDir, _ := filepath.EvalSymlinks(provider.WorkingDir())
  391. expectedOutput := fmt.Sprintf(
  392. "syncing to %s\n"+
  393. "%s\n"+
  394. "Done\n"+
  395. "syncing to %s\n"+
  396. "%s\n"+
  397. "Done\n",
  398. targetDir,
  399. fmt.Sprintf(
  400. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ --safe-links "+
  401. "--timeout=120 --exclude dists/ -6 "+
  402. "--exclude-from %s %s %s",
  403. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  404. ),
  405. targetDir,
  406. fmt.Sprintf(
  407. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ "+
  408. "--delete --delete-after --delay-updates --safe-links "+
  409. "--timeout=120 --delete-excluded --cache -6 --exclude-from %s %s %s",
  410. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  411. ),
  412. )
  413. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  414. So(err, ShouldBeNil)
  415. So(string(loggedContent), ShouldEqual, expectedOutput)
  416. // fmt.Println(string(loggedContent))
  417. })
  418. Convey("Try terminating", func(ctx C) {
  419. scriptContent := `#!/bin/bash
  420. echo $@
  421. sleep 4
  422. exit 0
  423. `
  424. err = ioutil.WriteFile(scriptFile, []byte(scriptContent), 0755)
  425. So(err, ShouldBeNil)
  426. go func() {
  427. err = provider.Run()
  428. ctx.So(err, ShouldNotBeNil)
  429. }()
  430. time.Sleep(1 * time.Second)
  431. err = provider.Terminate()
  432. So(err, ShouldBeNil)
  433. expectedOutput := fmt.Sprintf(
  434. "-aHvh --no-o --no-g --stats --exclude .~tmp~/ --safe-links "+
  435. "--timeout=120 --exclude dists/ -6 "+
  436. "--exclude-from %s %s %s\n",
  437. provider.excludeFile, provider.upstreamURL, provider.WorkingDir(),
  438. )
  439. loggedContent, err := ioutil.ReadFile(provider.LogFile())
  440. So(err, ShouldBeNil)
  441. So(string(loggedContent), ShouldEqual, expectedOutput)
  442. // fmt.Println(string(loggedContent))
  443. })
  444. })
  445. }