peer_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. Copyright 2014 CoreOS, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package etcdhttp
  14. import (
  15. "bytes"
  16. "encoding/json"
  17. "errors"
  18. "io"
  19. "net/http"
  20. "net/http/httptest"
  21. "path"
  22. "strconv"
  23. "strings"
  24. "testing"
  25. "github.com/coreos/etcd/etcdserver"
  26. "github.com/coreos/etcd/raft/raftpb"
  27. )
  28. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  29. json, err := m.Marshal()
  30. if err != nil {
  31. t.Fatalf("error marshalling raft Message: %#v", err)
  32. }
  33. return json
  34. }
  35. // errReader implements io.Reader to facilitate a broken request.
  36. type errReader struct{}
  37. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  38. func TestServeRaft(t *testing.T) {
  39. testCases := []struct {
  40. method string
  41. body io.Reader
  42. serverErr error
  43. clusterID string
  44. wcode int
  45. }{
  46. {
  47. // bad method
  48. "GET",
  49. bytes.NewReader(
  50. mustMarshalMsg(
  51. t,
  52. raftpb.Message{},
  53. ),
  54. ),
  55. nil,
  56. "0",
  57. http.StatusMethodNotAllowed,
  58. },
  59. {
  60. // bad method
  61. "PUT",
  62. bytes.NewReader(
  63. mustMarshalMsg(
  64. t,
  65. raftpb.Message{},
  66. ),
  67. ),
  68. nil,
  69. "0",
  70. http.StatusMethodNotAllowed,
  71. },
  72. {
  73. // bad method
  74. "DELETE",
  75. bytes.NewReader(
  76. mustMarshalMsg(
  77. t,
  78. raftpb.Message{},
  79. ),
  80. ),
  81. nil,
  82. "0",
  83. http.StatusMethodNotAllowed,
  84. },
  85. {
  86. // bad request body
  87. "POST",
  88. &errReader{},
  89. nil,
  90. "0",
  91. http.StatusBadRequest,
  92. },
  93. {
  94. // bad request protobuf
  95. "POST",
  96. strings.NewReader("malformed garbage"),
  97. nil,
  98. "0",
  99. http.StatusBadRequest,
  100. },
  101. {
  102. // good request, etcdserver.Server internal error
  103. "POST",
  104. bytes.NewReader(
  105. mustMarshalMsg(
  106. t,
  107. raftpb.Message{},
  108. ),
  109. ),
  110. errors.New("some error"),
  111. "0",
  112. http.StatusInternalServerError,
  113. },
  114. {
  115. // good request from removed member
  116. "POST",
  117. bytes.NewReader(
  118. mustMarshalMsg(
  119. t,
  120. raftpb.Message{},
  121. ),
  122. ),
  123. etcdserver.ErrRemoved,
  124. "0",
  125. http.StatusForbidden,
  126. },
  127. {
  128. // good request
  129. "POST",
  130. bytes.NewReader(
  131. mustMarshalMsg(
  132. t,
  133. raftpb.Message{},
  134. ),
  135. ),
  136. nil,
  137. "1",
  138. http.StatusPreconditionFailed,
  139. },
  140. {
  141. // good request
  142. "POST",
  143. bytes.NewReader(
  144. mustMarshalMsg(
  145. t,
  146. raftpb.Message{},
  147. ),
  148. ),
  149. nil,
  150. "0",
  151. http.StatusNoContent,
  152. },
  153. }
  154. for i, tt := range testCases {
  155. req, err := http.NewRequest(tt.method, "foo", tt.body)
  156. if err != nil {
  157. t.Fatalf("#%d: could not create request: %#v", i, err)
  158. }
  159. req.Header.Set("X-Etcd-Cluster-ID", tt.clusterID)
  160. rw := httptest.NewRecorder()
  161. h := &raftHandler{stats: nil, server: &errServer{tt.serverErr}, clusterInfo: &fakeCluster{id: 0}}
  162. h.ServeHTTP(rw, req)
  163. if rw.Code != tt.wcode {
  164. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  165. }
  166. }
  167. }
  168. func TestServeMembersFails(t *testing.T) {
  169. tests := []struct {
  170. method string
  171. wcode int
  172. }{
  173. {
  174. "POST",
  175. http.StatusMethodNotAllowed,
  176. },
  177. {
  178. "DELETE",
  179. http.StatusMethodNotAllowed,
  180. },
  181. {
  182. "BAD",
  183. http.StatusMethodNotAllowed,
  184. },
  185. }
  186. for i, tt := range tests {
  187. rw := httptest.NewRecorder()
  188. h := &peerMembersHandler{clusterInfo: nil}
  189. h.ServeHTTP(rw, &http.Request{Method: tt.method})
  190. if rw.Code != tt.wcode {
  191. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  192. }
  193. }
  194. }
  195. func TestServeMembersGet(t *testing.T) {
  196. memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
  197. memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
  198. cluster := &fakeCluster{
  199. id: 1,
  200. members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
  201. }
  202. h := &peerMembersHandler{clusterInfo: cluster}
  203. msb, err := json.Marshal([]etcdserver.Member{memb1, memb2})
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. wms := string(msb) + "\n"
  208. tests := []struct {
  209. path string
  210. wcode int
  211. wct string
  212. wbody string
  213. }{
  214. {peerMembersPrefix, http.StatusOK, "application/json", wms},
  215. {path.Join(peerMembersPrefix, "bad"), http.StatusBadRequest, "text/plain; charset=utf-8", "bad path\n"},
  216. }
  217. for i, tt := range tests {
  218. req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
  219. if err != nil {
  220. t.Fatal(err)
  221. }
  222. rw := httptest.NewRecorder()
  223. h.ServeHTTP(rw, req)
  224. if rw.Code != tt.wcode {
  225. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  226. }
  227. if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
  228. t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
  229. }
  230. if rw.Body.String() != tt.wbody {
  231. t.Errorf("#%d: body = %s, want %s", i, rw.Body.String(), tt.wbody)
  232. }
  233. gcid := rw.Header().Get("X-Etcd-Cluster-ID")
  234. wcid := strconv.FormatUint(cluster.ID(), 16)
  235. if gcid != wcid {
  236. t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
  237. }
  238. }
  239. }