discovery_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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.PeerURL("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. // TestDiscoveryFirstPeer ensures that etcd starts as the leader if it
  76. // registers as the first peer.
  77. func TestDiscoveryFirstPeer(t *testing.T) {
  78. etcdtest.RunServer(func(s *server.Server) {
  79. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  80. if err != nil {
  81. t.Fatal(err.Error())
  82. }
  83. defer stopServer(proc)
  84. client := http.Client{}
  85. err = assertServerFunctional(client, "http")
  86. if err != nil {
  87. t.Fatal(err.Error())
  88. }
  89. })
  90. }
  91. // TestDiscoverySecondPeerFirstDown ensures that etcd stops if it is started with a
  92. // correct discovery URL but no active machines are found.
  93. func TestDiscoverySecondPeerFirstDown(t *testing.T) {
  94. etcdtest.RunServer(func(s *server.Server) {
  95. v := url.Values{}
  96. v.Set("value", "started")
  97. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
  98. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  99. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  100. if err != nil {
  101. t.Fatal(err.Error())
  102. }
  103. defer stopServer(proc)
  104. client := http.Client{}
  105. err = assertServerNotUp(client, "http")
  106. if err != nil {
  107. t.Fatal(err.Error())
  108. }
  109. })
  110. }
  111. // TestDiscoverySecondPeerFirstNoResponse ensures that if the first etcd
  112. // machine stops after heartbeating that the second machine fails too.
  113. func TestDiscoverySecondPeerFirstNoResponse(t *testing.T) {
  114. etcdtest.RunServer(func(s *server.Server) {
  115. v := url.Values{}
  116. v.Set("value", "started")
  117. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/_state"), v)
  118. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  119. v = url.Values{}
  120. v.Set("value", "http://127.0.0.1:49151")
  121. resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/2/ETCDTEST"), v)
  122. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  123. proc, err := startServer([]string{"-retry-interval", "0.2", "-discovery", s.URL() + "/v2/keys/_etcd/registry/2"})
  124. if err != nil {
  125. t.Fatal(err.Error())
  126. }
  127. defer stopServer(proc)
  128. // TODO(bp): etcd will take 30 seconds to shutdown, figure this
  129. // out instead
  130. time.Sleep(1 * time.Second)
  131. client := http.Client{}
  132. _, err = client.Get("/")
  133. if err != nil && strings.Contains(err.Error(), "connection reset by peer") {
  134. t.Fatal(err.Error())
  135. }
  136. })
  137. }
  138. // TestDiscoverySecondPeerUp ensures that a second peer joining a discovery
  139. // cluster works.
  140. func TestDiscoverySecondPeerUp(t *testing.T) {
  141. etcdtest.RunServer(func(s *server.Server) {
  142. v := url.Values{}
  143. v.Set("value", "started")
  144. resp, err := etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/_state"), v)
  145. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  146. u, ok := s.PeerURL("ETCDTEST")
  147. if !ok {
  148. t.Fatalf("Couldn't find the URL")
  149. }
  150. wc := goetcd.NewClient([]string{s.URL()})
  151. testResp, err := wc.Set("test", "0", 0)
  152. if err != nil {
  153. t.Fatalf("Couldn't set a test key on the leader %v", err)
  154. }
  155. v = url.Values{}
  156. v.Set("value", u)
  157. resp, err = etcdtest.PutForm(fmt.Sprintf("%s%s", s.URL(), "/v2/keys/_etcd/registry/3/ETCDTEST"), v)
  158. assert.Equal(t, resp.StatusCode, http.StatusCreated)
  159. proc, err := startServer([]string{"-discovery", s.URL() + "/v2/keys/_etcd/registry/3"})
  160. if err != nil {
  161. t.Fatal(err.Error())
  162. }
  163. defer stopServer(proc)
  164. watch := fmt.Sprintf("%s%s%d", s.URL(), "/v2/keys/_etcd/registry/3/node1?wait=true&waitIndex=", testResp.EtcdIndex)
  165. resp, err = http.Get(watch)
  166. if err != nil {
  167. t.Fatal(err.Error())
  168. }
  169. // TODO(bp): need to have a better way of knowing a machine is up
  170. for i := 0; i < 10; i++ {
  171. time.Sleep(1 * time.Second)
  172. etcdc := goetcd.NewClient(nil)
  173. _, err = etcdc.Set("foobar", "baz", 0)
  174. if err == nil {
  175. break
  176. }
  177. }
  178. if err != nil {
  179. t.Fatal(err.Error())
  180. }
  181. })
  182. }
  183. func assertServerNotUp(client http.Client, scheme string) error {
  184. path := fmt.Sprintf("%s://127.0.0.1:4001/v2/keys/foo", scheme)
  185. fields := url.Values(map[string][]string{"value": []string{"bar"}})
  186. for i := 0; i < 10; i++ {
  187. time.Sleep(1 * time.Second)
  188. _, err := client.PostForm(path, fields)
  189. if err == nil {
  190. return errors.New("Expected error during POST, got nil")
  191. } else {
  192. errString := err.Error()
  193. if strings.Contains(errString, "connection refused") {
  194. return nil
  195. } else {
  196. return err
  197. }
  198. }
  199. }
  200. return nil
  201. }