discovery_test.go 9.4 KB

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