syncthingprocess.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at http://mozilla.org/MPL/2.0/.
  6. // +build integration
  7. package integration
  8. import (
  9. "bufio"
  10. "bytes"
  11. "encoding/json"
  12. "errors"
  13. "fmt"
  14. "io"
  15. "net/http"
  16. "os"
  17. "os/exec"
  18. "time"
  19. )
  20. var env = []string{
  21. "HOME=.",
  22. "STGUIAPIKEY=" + apiKey,
  23. "STNORESTART=1",
  24. }
  25. type syncthingProcess struct {
  26. instance string
  27. argv []string
  28. port int
  29. apiKey string
  30. csrfToken string
  31. lastEvent int
  32. cmd *exec.Cmd
  33. logfd *os.File
  34. }
  35. func (p *syncthingProcess) start() error {
  36. if p.logfd == nil {
  37. logfd, err := os.Create("logs/" + getTestName() + "-" + p.instance + ".out")
  38. if err != nil {
  39. return err
  40. }
  41. p.logfd = logfd
  42. }
  43. binary := "../bin/syncthing"
  44. // We check to see if there's an instance specific binary we should run,
  45. // for example if we are running integration tests between different
  46. // versions. If there isn't, we just go with the default.
  47. if _, err := os.Stat(binary + "-" + p.instance); err == nil {
  48. binary = binary + "-" + p.instance
  49. }
  50. if _, err := os.Stat(binary + "-" + p.instance + ".exe"); err == nil {
  51. binary = binary + "-" + p.instance + ".exe"
  52. }
  53. cmd := exec.Command(binary, p.argv...)
  54. cmd.Stdout = p.logfd
  55. cmd.Stderr = p.logfd
  56. cmd.Env = append(os.Environ(), env...)
  57. err := cmd.Start()
  58. if err != nil {
  59. return err
  60. }
  61. p.cmd = cmd
  62. for {
  63. resp, err := p.get("/")
  64. if err == nil {
  65. resp.Body.Close()
  66. return nil
  67. }
  68. time.Sleep(250 * time.Millisecond)
  69. }
  70. }
  71. func (p *syncthingProcess) stop() error {
  72. p.cmd.Process.Signal(os.Kill)
  73. p.cmd.Wait()
  74. fd, err := os.Open(p.logfd.Name())
  75. if err != nil {
  76. return err
  77. }
  78. defer fd.Close()
  79. raceConditionStart := []byte("WARNING: DATA RACE")
  80. raceConditionSep := []byte("==================")
  81. sc := bufio.NewScanner(fd)
  82. race := false
  83. for sc.Scan() {
  84. line := sc.Bytes()
  85. if race {
  86. fmt.Printf("%s\n", line)
  87. if bytes.Contains(line, raceConditionSep) {
  88. race = false
  89. }
  90. } else if bytes.Contains(line, raceConditionStart) {
  91. fmt.Printf("%s\n", raceConditionSep)
  92. fmt.Printf("%s\n", raceConditionStart)
  93. race = true
  94. if err == nil {
  95. err = errors.New("Race condition detected")
  96. }
  97. }
  98. }
  99. return err
  100. }
  101. func (p *syncthingProcess) get(path string) (*http.Response, error) {
  102. client := &http.Client{
  103. Timeout: 30 * time.Second,
  104. Transport: &http.Transport{
  105. DisableKeepAlives: true,
  106. },
  107. }
  108. req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:%d%s", p.port, path), nil)
  109. if err != nil {
  110. return nil, err
  111. }
  112. if p.apiKey != "" {
  113. req.Header.Add("X-API-Key", p.apiKey)
  114. }
  115. if p.csrfToken != "" {
  116. req.Header.Add("X-CSRF-Token", p.csrfToken)
  117. }
  118. resp, err := client.Do(req)
  119. if err != nil {
  120. return nil, err
  121. }
  122. return resp, nil
  123. }
  124. func (p *syncthingProcess) post(path string, data io.Reader) (*http.Response, error) {
  125. client := &http.Client{
  126. Timeout: 600 * time.Second,
  127. Transport: &http.Transport{
  128. DisableKeepAlives: true,
  129. },
  130. }
  131. req, err := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d%s", p.port, path), data)
  132. if err != nil {
  133. return nil, err
  134. }
  135. if p.apiKey != "" {
  136. req.Header.Add("X-API-Key", p.apiKey)
  137. }
  138. if p.csrfToken != "" {
  139. req.Header.Add("X-CSRF-Token", p.csrfToken)
  140. }
  141. req.Header.Add("Content-Type", "application/json")
  142. resp, err := client.Do(req)
  143. if err != nil {
  144. return nil, err
  145. }
  146. return resp, nil
  147. }
  148. func (p *syncthingProcess) peerCompletion() (map[string]int, error) {
  149. resp, err := p.get("/rest/debug/peerCompletion")
  150. if err != nil {
  151. return nil, err
  152. }
  153. defer resp.Body.Close()
  154. comp := map[string]int{}
  155. err = json.NewDecoder(resp.Body).Decode(&comp)
  156. return comp, err
  157. }
  158. type model struct {
  159. GlobalBytes int
  160. GlobalDeleted int
  161. GlobalFiles int
  162. InSyncBytes int
  163. InSyncFiles int
  164. Invalid string
  165. LocalBytes int
  166. LocalDeleted int
  167. LocalFiles int
  168. NeedBytes int
  169. NeedFiles int
  170. State string
  171. StateChanged time.Time
  172. Version int
  173. }
  174. func (p *syncthingProcess) model(folder string) (model, error) {
  175. resp, err := p.get("/rest/model?folder=" + folder)
  176. if err != nil {
  177. return model{}, err
  178. }
  179. var res model
  180. err = json.NewDecoder(resp.Body).Decode(&res)
  181. if err != nil {
  182. return model{}, err
  183. }
  184. return res, nil
  185. }
  186. type event struct {
  187. ID int
  188. Time time.Time
  189. Type string
  190. Data interface{}
  191. }
  192. func (p *syncthingProcess) events() ([]event, error) {
  193. resp, err := p.get(fmt.Sprintf("/rest/events?since=%d", p.lastEvent))
  194. if err != nil {
  195. return nil, err
  196. }
  197. defer resp.Body.Close()
  198. var evs []event
  199. err = json.NewDecoder(resp.Body).Decode(&evs)
  200. if err != nil {
  201. return nil, err
  202. }
  203. p.lastEvent = evs[len(evs)-1].ID
  204. return evs, err
  205. }
  206. type versionResp struct {
  207. Version string
  208. }
  209. func (p *syncthingProcess) version() (string, error) {
  210. resp, err := p.get("/rest/version")
  211. if err != nil {
  212. return "", err
  213. }
  214. defer resp.Body.Close()
  215. var v versionResp
  216. err = json.NewDecoder(resp.Body).Decode(&v)
  217. if err != nil {
  218. return "", err
  219. }
  220. return v.Version, nil
  221. }