syncthingprocess.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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(p.instance + ".out")
  47. if err != nil {
  48. return err
  49. }
  50. p.logfd = logfd
  51. }
  52. cmd := exec.Command("../bin/syncthing", p.argv...)
  53. cmd.Stdout = p.logfd
  54. cmd.Stderr = p.logfd
  55. cmd.Env = append(os.Environ(), env...)
  56. err := cmd.Start()
  57. if err != nil {
  58. return err
  59. }
  60. p.cmd = cmd
  61. for {
  62. resp, err := p.get("/")
  63. if err == nil {
  64. resp.Body.Close()
  65. return nil
  66. }
  67. time.Sleep(250 * time.Millisecond)
  68. }
  69. }
  70. func (p *syncthingProcess) stop() error {
  71. p.cmd.Process.Signal(os.Kill)
  72. p.cmd.Wait()
  73. fd, err := os.Open(p.instance + ".out")
  74. if err != nil {
  75. return err
  76. }
  77. defer fd.Close()
  78. raceConditionStart := []byte("WARNING: DATA RACE")
  79. raceConditionSep := []byte("==================")
  80. sc := bufio.NewScanner(fd)
  81. race := false
  82. for sc.Scan() {
  83. line := sc.Bytes()
  84. if race {
  85. fmt.Printf("%s\n", line)
  86. if bytes.Contains(line, raceConditionSep) {
  87. race = false
  88. }
  89. } else if bytes.Contains(line, raceConditionStart) {
  90. fmt.Printf("%s\n", raceConditionSep)
  91. fmt.Printf("%s\n", raceConditionStart)
  92. race = true
  93. if err == nil {
  94. err = errors.New("Race condition detected")
  95. }
  96. }
  97. }
  98. return err
  99. }
  100. func (p *syncthingProcess) get(path string) (*http.Response, error) {
  101. client := &http.Client{
  102. Timeout: 30 * time.Second,
  103. Transport: &http.Transport{
  104. DisableKeepAlives: true,
  105. },
  106. }
  107. req, err := http.NewRequest("GET", fmt.Sprintf("http://127.0.0.1:%d%s", p.port, path), nil)
  108. if err != nil {
  109. return nil, err
  110. }
  111. if p.apiKey != "" {
  112. req.Header.Add("X-API-Key", p.apiKey)
  113. }
  114. if p.csrfToken != "" {
  115. req.Header.Add("X-CSRF-Token", p.csrfToken)
  116. }
  117. resp, err := client.Do(req)
  118. if err != nil {
  119. return nil, err
  120. }
  121. return resp, nil
  122. }
  123. func (p *syncthingProcess) post(path string, data io.Reader) (*http.Response, error) {
  124. client := &http.Client{
  125. Timeout: 600 * time.Second,
  126. Transport: &http.Transport{
  127. DisableKeepAlives: true,
  128. },
  129. }
  130. req, err := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d%s", p.port, path), data)
  131. if err != nil {
  132. return nil, err
  133. }
  134. if p.apiKey != "" {
  135. req.Header.Add("X-API-Key", p.apiKey)
  136. }
  137. if p.csrfToken != "" {
  138. req.Header.Add("X-CSRF-Token", p.csrfToken)
  139. }
  140. req.Header.Add("Content-Type", "application/json")
  141. resp, err := client.Do(req)
  142. if err != nil {
  143. return nil, err
  144. }
  145. return resp, nil
  146. }
  147. func (p *syncthingProcess) peerCompletion() (map[string]int, error) {
  148. resp, err := p.get("/rest/debug/peerCompletion")
  149. if err != nil {
  150. return nil, err
  151. }
  152. defer resp.Body.Close()
  153. comp := map[string]int{}
  154. err = json.NewDecoder(resp.Body).Decode(&comp)
  155. return comp, err
  156. }
  157. type model struct {
  158. GlobalBytes int
  159. GlobalDeleted int
  160. GlobalFiles int
  161. InSyncBytes int
  162. InSyncFiles int
  163. Invalid string
  164. LocalBytes int
  165. LocalDeleted int
  166. LocalFiles int
  167. NeedBytes int
  168. NeedFiles int
  169. State string
  170. StateChanged time.Time
  171. Version int
  172. }
  173. func (p *syncthingProcess) model(folder string) (model, error) {
  174. resp, err := p.get("/rest/model?folder=" + folder)
  175. if err != nil {
  176. return model{}, err
  177. }
  178. var res model
  179. err = json.NewDecoder(resp.Body).Decode(&res)
  180. if err != nil {
  181. return model{}, err
  182. }
  183. return res, nil
  184. }
  185. type event struct {
  186. ID int
  187. Time time.Time
  188. Type string
  189. Data interface{}
  190. }
  191. func (p *syncthingProcess) events() ([]event, error) {
  192. resp, err := p.get(fmt.Sprintf("/rest/events?since=%d", p.lastEvent))
  193. if err != nil {
  194. return nil, err
  195. }
  196. defer resp.Body.Close()
  197. var evs []event
  198. err = json.NewDecoder(resp.Body).Decode(&evs)
  199. if err != nil {
  200. return nil, err
  201. }
  202. p.lastEvent = evs[len(evs)-1].ID
  203. return evs, err
  204. }
  205. type versionResp struct {
  206. Version string
  207. }
  208. func (p *syncthingProcess) version() (string, error) {
  209. resp, err := p.get("/rest/version")
  210. if err != nil {
  211. return "", err
  212. }
  213. defer resp.Body.Close()
  214. var v versionResp
  215. err = json.NewDecoder(resp.Body).Decode(&v)
  216. if err != nil {
  217. return "", err
  218. }
  219. return v.Version, nil
  220. }