peer_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. "time"
  26. "github.com/coreos/etcd/etcdserver"
  27. "github.com/coreos/etcd/raft/raftpb"
  28. )
  29. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  30. json, err := m.Marshal()
  31. if err != nil {
  32. t.Fatalf("error marshalling raft Message: %#v", err)
  33. }
  34. return json
  35. }
  36. // errReader implements io.Reader to facilitate a broken request.
  37. type errReader struct{}
  38. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  39. func TestServeRaft(t *testing.T) {
  40. testCases := []struct {
  41. method string
  42. body io.Reader
  43. serverErr error
  44. clusterID string
  45. wcode int
  46. }{
  47. {
  48. // bad method
  49. "GET",
  50. bytes.NewReader(
  51. mustMarshalMsg(
  52. t,
  53. raftpb.Message{},
  54. ),
  55. ),
  56. nil,
  57. "0",
  58. http.StatusMethodNotAllowed,
  59. },
  60. {
  61. // bad method
  62. "PUT",
  63. bytes.NewReader(
  64. mustMarshalMsg(
  65. t,
  66. raftpb.Message{},
  67. ),
  68. ),
  69. nil,
  70. "0",
  71. http.StatusMethodNotAllowed,
  72. },
  73. {
  74. // bad method
  75. "DELETE",
  76. bytes.NewReader(
  77. mustMarshalMsg(
  78. t,
  79. raftpb.Message{},
  80. ),
  81. ),
  82. nil,
  83. "0",
  84. http.StatusMethodNotAllowed,
  85. },
  86. {
  87. // bad request body
  88. "POST",
  89. &errReader{},
  90. nil,
  91. "0",
  92. http.StatusBadRequest,
  93. },
  94. {
  95. // bad request protobuf
  96. "POST",
  97. strings.NewReader("malformed garbage"),
  98. nil,
  99. "0",
  100. http.StatusBadRequest,
  101. },
  102. {
  103. // good request, etcdserver.Server internal error
  104. "POST",
  105. bytes.NewReader(
  106. mustMarshalMsg(
  107. t,
  108. raftpb.Message{},
  109. ),
  110. ),
  111. errors.New("some error"),
  112. "0",
  113. http.StatusInternalServerError,
  114. },
  115. {
  116. // good request from removed member
  117. "POST",
  118. bytes.NewReader(
  119. mustMarshalMsg(
  120. t,
  121. raftpb.Message{},
  122. ),
  123. ),
  124. etcdserver.ErrRemoved,
  125. "0",
  126. http.StatusForbidden,
  127. },
  128. {
  129. // good request
  130. "POST",
  131. bytes.NewReader(
  132. mustMarshalMsg(
  133. t,
  134. raftpb.Message{},
  135. ),
  136. ),
  137. nil,
  138. "1",
  139. http.StatusPreconditionFailed,
  140. },
  141. {
  142. // good request
  143. "POST",
  144. bytes.NewReader(
  145. mustMarshalMsg(
  146. t,
  147. raftpb.Message{},
  148. ),
  149. ),
  150. nil,
  151. "0",
  152. http.StatusNoContent,
  153. },
  154. }
  155. for i, tt := range testCases {
  156. req, err := http.NewRequest(tt.method, "foo", tt.body)
  157. if err != nil {
  158. t.Fatalf("#%d: could not create request: %#v", i, err)
  159. }
  160. req.Header.Set("X-Etcd-Cluster-ID", tt.clusterID)
  161. h := &serverHandler{
  162. timeout: time.Hour,
  163. server: &errServer{tt.serverErr},
  164. clusterInfo: &fakeCluster{id: 0},
  165. }
  166. rw := httptest.NewRecorder()
  167. h.serveRaft(rw, req)
  168. if rw.Code != tt.wcode {
  169. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  170. }
  171. }
  172. }
  173. func TestServeMembersFails(t *testing.T) {
  174. tests := []struct {
  175. method string
  176. wcode int
  177. }{
  178. {
  179. "POST",
  180. http.StatusMethodNotAllowed,
  181. },
  182. {
  183. "DELETE",
  184. http.StatusMethodNotAllowed,
  185. },
  186. {
  187. "BAD",
  188. http.StatusMethodNotAllowed,
  189. },
  190. }
  191. for i, tt := range tests {
  192. h := &serverHandler{}
  193. rw := httptest.NewRecorder()
  194. h.serveMembers(rw, &http.Request{Method: tt.method})
  195. if rw.Code != tt.wcode {
  196. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  197. }
  198. }
  199. }
  200. func TestServeMembersGet(t *testing.T) {
  201. memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
  202. memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
  203. cluster := &fakeCluster{
  204. id: 1,
  205. members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
  206. }
  207. h := &serverHandler{
  208. server: &serverRecorder{},
  209. clusterInfo: cluster,
  210. }
  211. msb, err := json.Marshal([]etcdserver.Member{memb1, memb2})
  212. if err != nil {
  213. t.Fatal(err)
  214. }
  215. wms := string(msb) + "\n"
  216. tests := []struct {
  217. path string
  218. wcode int
  219. wct string
  220. wbody string
  221. }{
  222. {membersPrefix, http.StatusOK, "application/json", wms},
  223. {path.Join(membersPrefix, "bad"), http.StatusBadRequest, "text/plain; charset=utf-8", "bad path\n"},
  224. }
  225. for i, tt := range tests {
  226. req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
  227. if err != nil {
  228. t.Fatal(err)
  229. }
  230. rw := httptest.NewRecorder()
  231. h.serveMembers(rw, req)
  232. if rw.Code != tt.wcode {
  233. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  234. }
  235. if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
  236. t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
  237. }
  238. if rw.Body.String() != tt.wbody {
  239. t.Errorf("#%d: body = %s, want %s", i, rw.Body.String(), tt.wbody)
  240. }
  241. gcid := rw.Header().Get("X-Etcd-Cluster-ID")
  242. wcid := strconv.FormatUint(cluster.ID(), 16)
  243. if gcid != wcid {
  244. t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
  245. }
  246. }
  247. }