syncthingprocess.go 5.4 KB

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