discovery_test.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package test
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "net/http/httptest"
  7. "net/url"
  8. "strings"
  9. "testing"
  10. "time"
  11. "github.com/coreos/etcd/third_party/github.com/stretchr/testify/assert"
  12. etcdtest "github.com/coreos/etcd/tests"
  13. "github.com/coreos/etcd/server"
  14. goetcd "github.com/coreos/etcd/third_party/github.com/coreos/go-etcd/etcd"
  15. )
  16. type garbageHandler struct {
  17. t *testing.T
  18. success bool
  19. }
  20. func (g *garbageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  21. fmt.Fprintln(w, "Hello, client")
  22. if r.URL.String() != "/v2/keys/_etcd/registry/1/node1" {
  23. g.t.Fatalf("Unexpected web request")
  24. }
  25. g.success = true
  26. }
  27. // TestDiscoveryDownNoBackupPeers ensures that etcd stops if it is started with a
  28. // bad discovery URL and no backups.
  29. func TestDiscoveryDownNoBackupPeers(t *testing.T) {
  30. g := garbageHandler{t: t}
  31. ts := httptest.NewServer(&g)
  32. defer ts.Close()
  33. discover := ts.URL + "/v2/keys/_etcd/registry/1"
  34. proc, err := startServer([]string{"-discovery", discover})
  35. if err != nil {
  36. t.Fatal(err.Error())
  37. }
  38. defer stopServer(proc)
  39. client := http.Client{}
  40. err = assertServerNotUp(client, "http")
  41. if err != nil {
  42. t.Fatal(err.Error())
  43. }
  44. if !g.success {
  45. t.Fatal("Discovery server never called")
  46. }
  47. }
  48. // TestDiscoveryDownWithBackupPeers ensures that etcd runs if it is started with a
  49. // bad discovery URL and a peer list.
  50. func TestDiscoveryDownWithBackupPeers(t *testing.T) {
  51. etcdtest.RunServer(func(s *server.Server) {
  52. g := garbageHandler{t: t}
  53. ts := httptest.NewServer(&g)
  54. defer ts.Close()
  55. discover := ts.URL + "/v2/keys/_etcd/registry/1"
  56. u, ok := s.PeerHost("ETCDTEST")
  57. if !ok {
  58. t.Fatalf("Couldn't find the URL")
  59. }
  60. proc, err := startServer([]string{"-discovery", discover, "-peers", u})
  61. if err != nil {
  62. t.Fatal(err.Error())
  63. }
  64. defer stopServer(proc)
  65. client := http.Client{}
  66. err = assertServerFunctional(client, "http")
  67. if err != nil {
  68. t.Fatal(err.Error())
  69. }
  70. if !g.success {
  71. t.Fatal("Discovery server never called")
  72. }
  73. })
  74. }
  75. // TestDiscoveryNoWithBackupPeers ensures that etcd runs if it is started with
  76. // no discovery URL and a peer list.
  77. func TestDiscoveryNoWithBackupPeers(t *testing.T) {
  78. etcdtest.RunServer(func(s *server.Server) {
  79. u, ok := s.PeerHost("ETCDTEST")
  80. if !ok {
  81. t.Fatalf("Couldn't find the URL")
  82. }
  83. proc, err := startServer([]string{"-peers", u})
  84. if err != nil {
  85. t.Fatal(err.Error())
  86. }
  87. defer stopServer(proc)
  88. client := http.Client{}
  89. err = assertServerFunctional(client, "http")
  90. if err != nil {
  91. t.Fatal(err.Error())
  92. }
  93. })
  94. }
  95. // TestDiscoveryDownNoBackupPeersWithDataDir ensures that etcd runs if it is
  96. // started with a bad discovery URL, no backups and valid data dir.
  97. func TestDiscoveryDownNoBackupPeersWithDataDir(t *testing.T) {
  98. etcdtest.RunServer(func(s *server.Server) {
  99. u, ok := s.PeerHost("ETCDTEST")
  100. if !ok {
  101. t.Fatalf("Couldn't find the URL")
  102. }
  103. // run etcd and connect to ETCDTEST server
  104. proc, err := startServer([]string{"-peers", u})
  105. if err != nil {
  106. t.Fatal(err.Error())
  107. }
  108. // check it runs well
  109. client := http.Client{}
  110. err = assertServerFunctional(client, "http")
  111. if err != nil {
  112. t.Fatal(err.Error())
  113. }
  114. // stop etcd, and leave valid data dir for later usage
  115. stopServer(proc)
  116. g := garbageHandler{t: t}
  117. ts := httptest.NewServer(&g)
  118. defer ts.Close()
  119. discover := ts.URL + "/v2/keys/_etcd/registry/1"
  120. // connect to ETCDTEST server again with previous data dir
  121. proc, err = startServerWithDataDir([]string{"-discovery", discover})
  122. if err != nil {
  123. t.Fatal(err.Error())
  124. }
  125. defer stopServer(proc)
  126. // TODO(yichengq): it needs some time to do leader election
  127. // improve to get rid of it
  128. time.Sleep(1 * time.Second)
  129. client = http.Client{}
  130. err = assertServerFunctional(client, "http")
  131. if err != nil {
  132. t.Fatal(err.Error())
  133. }
  134. if !g.success {
  135. t.Fatal("Discovery server never called")
  136. }
  137. })
  138. }
  139. // TestDiscoveryFirstPeer ensures that etcd starts as the leader if it
  140. // registers as the first peer.
  141. func TestDiscoveryFirstPeer(t *testing.T) {
  142. etcdtest.RunServer(func(s *server.Server) {
  143. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  144. if err != nil {
  145. t.Fatal(err.Error())
  146. }
  147. defer stopServer(proc)
  148. client := http.Client{}
  149. err = assertServerFunctional(client, "http")
  150. if err != nil {
  151. t.Fatal(err.Error())
  152. }
  153. })
  154. }
  155. // TestDiscoverySecondPeerFirstDown ensures that etcd stops if it is started with a
  156. // correct discovery URL but no active machines are found.
  157. func TestDiscoverySecondPeerFirstDown(t *testing.T) {
  158. etcdtest.RunServer(func(s *server.Server) {
  159. v := url.Values{}
  160. v.Set("value", "started")
  161. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
  162. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  163. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  164. if err != nil {
  165. t.Fatal(err.Error())
  166. }
  167. defer stopServer(proc)
  168. client := http.Client{}
  169. err = assertServerNotUp(client, "http")
  170. if err != nil {
  171. t.Fatal(err.Error())
  172. }
  173. })
  174. }
  175. // TestDiscoverySecondPeerFirstNoResponse ensures that if the first etcd
  176. // machine stops after heartbeating that the second machine fails too.
  177. func TestDiscoverySecondPeerFirstNoResponse(t *testing.T) {
  178. etcdtest.RunServer(func(s *server.Server) {
  179. v := url.Values{}
  180. v.Set("value", "started")
  181. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
  182. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  183. v = url.Values{}
  184. v.Set("value", "http://127.0.0.1:49151")
  185. resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/ETCDTEST"), v)
  186. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  187. proc, err := startServer([]string{"-retry-interval", "0.2", "-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  188. if err != nil {
  189. t.Fatal(err.Error())
  190. }
  191. defer stopServer(proc)
  192. // TODO(bp): etcd will take 30 seconds to shutdown, figure this
  193. // out instead
  194. time.Sleep(1 * time.Second)
  195. client := http.Client{}
  196. _, err = client.Get("/")
  197. if err != nil && strings.Contains(err.Error(), "connection reset by peer") {
  198. t.Fatal(err.Error())
  199. }
  200. })
  201. }
  202. // TestDiscoverySecondPeerUp ensures that a second peer joining a discovery
  203. // cluster works.
  204. func TestDiscoverySecondPeerUp(t *testing.T) {
  205. etcdtest.RunServer(func(s *server.Server) {
  206. v := url.Values{}
  207. v.Set("value", "started")
  208. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/_state"), v)
  209. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  210. u, ok := s.PeerURL("ETCDTEST")
  211. if !ok {
  212. t.Fatalf("Couldn't find the URL")
  213. }
  214. wc := goetcd.NewClient([]string{s.URL()})
  215. testResp, err := wc.Set("test", "0", 0)
  216. if err != nil {
  217. t.Fatalf("Couldn't set a test key on the leader %v", err)
  218. }
  219. v = url.Values{}
  220. v.Set("value", u)
  221. resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/ETCDTEST"), v)
  222. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  223. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/3"})
  224. if err != nil {
  225. t.Fatal(err.Error())
  226. }
  227. defer stopServer(proc)
  228. watch := fmt.Sprintf("%s%s%d", s.URL(), "/v2/keys/_etcd/registry/3/node1?wait=true&waitIndex=", testResp.EtcdIndex)
  229. resp, err = http.Get(watch)
  230. if err != nil {
  231. t.Fatal(err.Error())
  232. }
  233. // TODO(bp): need to have a better way of knowing a machine is up
  234. for i := 0; i < 10; i++ {
  235. time.Sleep(1 * time.Second)
  236. etcdc := goetcd.NewClient(nil)
  237. _, err = etcdc.Set("foobar", "baz", 0)
  238. if err == nil {
  239. break
  240. }
  241. }
  242. if err != nil {
  243. t.Fatal(err.Error())
  244. }
  245. })
  246. }
  247. func assertServerNotUp(client http.Client, scheme string) error {
  248. path := fmt.Sprintf("%s://127.0.0.1:4001/v2/keys/foo", scheme)
  249. fields := url.Values(map[string][]string{"value": []string{"bar"}})
  250. for i := 0; i < 10; i++ {
  251. time.Sleep(1 * time.Second)
  252. _, err := client.PostForm(path, fields)
  253. if err == nil {
  254. return errors.New("Expected error during POST, got nil")
  255. } else {
  256. errString := err.Error()
  257. if strings.Contains(errString, "connection refused") {
  258. return nil
  259. } else {
  260. return err
  261. }
  262. }
  263. }
  264. return nil
  265. }