provider_test.go 15 KB

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