http_test.go 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  1. package etcdhttp
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "net/http/httptest"
  10. "net/url"
  11. "path"
  12. "reflect"
  13. "strings"
  14. "testing"
  15. "time"
  16. etcdErr "github.com/coreos/etcd/error"
  17. "github.com/coreos/etcd/etcdserver"
  18. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  19. "github.com/coreos/etcd/raft/raftpb"
  20. "github.com/coreos/etcd/store"
  21. "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
  22. )
  23. func boolp(b bool) *bool { return &b }
  24. func mustNewURL(t *testing.T, s string) *url.URL {
  25. u, err := url.Parse(s)
  26. if err != nil {
  27. t.Fatalf("error creating URL from %q: %v", s, err)
  28. }
  29. return u
  30. }
  31. // mustNewRequest takes a path, appends it to the standard keysPrefix, and constructs
  32. // a GET *http.Request referencing the resulting URL
  33. func mustNewRequest(t *testing.T, p string) *http.Request {
  34. return mustNewMethodRequest(t, "GET", p)
  35. }
  36. func mustNewMethodRequest(t *testing.T, m, p string) *http.Request {
  37. return &http.Request{
  38. Method: m,
  39. URL: mustNewURL(t, path.Join(keysPrefix, p)),
  40. }
  41. }
  42. // mustNewForm takes a set of Values and constructs a PUT *http.Request,
  43. // with a URL constructed from appending the given path to the standard keysPrefix
  44. func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
  45. u := mustNewURL(t, path.Join(keysPrefix, p))
  46. req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
  47. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  48. if err != nil {
  49. t.Fatalf("error creating new request: %v", err)
  50. }
  51. return req
  52. }
  53. func TestBadParseRequest(t *testing.T) {
  54. tests := []struct {
  55. in *http.Request
  56. wcode int
  57. }{
  58. {
  59. // parseForm failure
  60. &http.Request{
  61. Body: nil,
  62. Method: "PUT",
  63. },
  64. etcdErr.EcodeInvalidForm,
  65. },
  66. {
  67. // bad key prefix
  68. &http.Request{
  69. URL: mustNewURL(t, "/badprefix/"),
  70. },
  71. etcdErr.EcodeInvalidForm,
  72. },
  73. // bad values for prevIndex, waitIndex, ttl
  74. {
  75. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"garbage"}}),
  76. etcdErr.EcodeIndexNaN,
  77. },
  78. {
  79. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"1.5"}}),
  80. etcdErr.EcodeIndexNaN,
  81. },
  82. {
  83. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"-1"}}),
  84. etcdErr.EcodeIndexNaN,
  85. },
  86. {
  87. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"garbage"}}),
  88. etcdErr.EcodeIndexNaN,
  89. },
  90. {
  91. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"??"}}),
  92. etcdErr.EcodeIndexNaN,
  93. },
  94. {
  95. mustNewForm(t, "foo", url.Values{"ttl": []string{"-1"}}),
  96. etcdErr.EcodeTTLNaN,
  97. },
  98. // bad values for recursive, sorted, wait, prevExist, dir, stream
  99. {
  100. mustNewForm(t, "foo", url.Values{"recursive": []string{"hahaha"}}),
  101. etcdErr.EcodeInvalidField,
  102. },
  103. {
  104. mustNewForm(t, "foo", url.Values{"recursive": []string{"1234"}}),
  105. etcdErr.EcodeInvalidField,
  106. },
  107. {
  108. mustNewForm(t, "foo", url.Values{"recursive": []string{"?"}}),
  109. etcdErr.EcodeInvalidField,
  110. },
  111. {
  112. mustNewForm(t, "foo", url.Values{"sorted": []string{"?"}}),
  113. etcdErr.EcodeInvalidField,
  114. },
  115. {
  116. mustNewForm(t, "foo", url.Values{"sorted": []string{"x"}}),
  117. etcdErr.EcodeInvalidField,
  118. },
  119. {
  120. mustNewForm(t, "foo", url.Values{"wait": []string{"?!"}}),
  121. etcdErr.EcodeInvalidField,
  122. },
  123. {
  124. mustNewForm(t, "foo", url.Values{"wait": []string{"yes"}}),
  125. etcdErr.EcodeInvalidField,
  126. },
  127. {
  128. mustNewForm(t, "foo", url.Values{"prevExist": []string{"yes"}}),
  129. etcdErr.EcodeInvalidField,
  130. },
  131. {
  132. mustNewForm(t, "foo", url.Values{"prevExist": []string{"#2"}}),
  133. etcdErr.EcodeInvalidField,
  134. },
  135. {
  136. mustNewForm(t, "foo", url.Values{"dir": []string{"no"}}),
  137. etcdErr.EcodeInvalidField,
  138. },
  139. {
  140. mustNewForm(t, "foo", url.Values{"dir": []string{"file"}}),
  141. etcdErr.EcodeInvalidField,
  142. },
  143. {
  144. mustNewForm(t, "foo", url.Values{"stream": []string{"zzz"}}),
  145. etcdErr.EcodeInvalidField,
  146. },
  147. {
  148. mustNewForm(t, "foo", url.Values{"stream": []string{"something"}}),
  149. etcdErr.EcodeInvalidField,
  150. },
  151. // prevValue cannot be empty
  152. {
  153. mustNewForm(t, "foo", url.Values{"prevValue": []string{""}}),
  154. etcdErr.EcodeInvalidField,
  155. },
  156. // wait is only valid with GET requests
  157. {
  158. mustNewMethodRequest(t, "HEAD", "foo?wait=true"),
  159. etcdErr.EcodeInvalidField,
  160. },
  161. // query values are considered
  162. {
  163. mustNewRequest(t, "foo?prevExist=wrong"),
  164. etcdErr.EcodeInvalidField,
  165. },
  166. {
  167. mustNewRequest(t, "foo?ttl=wrong"),
  168. etcdErr.EcodeTTLNaN,
  169. },
  170. // but body takes precedence if both are specified
  171. {
  172. mustNewForm(
  173. t,
  174. "foo?ttl=12",
  175. url.Values{"ttl": []string{"garbage"}},
  176. ),
  177. etcdErr.EcodeTTLNaN,
  178. },
  179. {
  180. mustNewForm(
  181. t,
  182. "foo?prevExist=false",
  183. url.Values{"prevExist": []string{"yes"}},
  184. ),
  185. etcdErr.EcodeInvalidField,
  186. },
  187. }
  188. for i, tt := range tests {
  189. got, err := parseRequest(tt.in, 1234)
  190. if err == nil {
  191. t.Errorf("#%d: unexpected nil error!", i)
  192. continue
  193. }
  194. ee, ok := err.(*etcdErr.Error)
  195. if !ok {
  196. t.Errorf("#%d: err is not etcd.Error!", i)
  197. continue
  198. }
  199. if ee.ErrorCode != tt.wcode {
  200. t.Errorf("#%d: code=%d, want %v", i, ee.ErrorCode, tt.wcode)
  201. t.Logf("cause: %#v", ee.Cause)
  202. }
  203. if !reflect.DeepEqual(got, etcdserverpb.Request{}) {
  204. t.Errorf("#%d: unexpected non-empty Request: %#v", i, got)
  205. }
  206. }
  207. }
  208. func TestGoodParseRequest(t *testing.T) {
  209. tests := []struct {
  210. in *http.Request
  211. w etcdserverpb.Request
  212. }{
  213. {
  214. // good prefix, all other values default
  215. mustNewRequest(t, "foo"),
  216. etcdserverpb.Request{
  217. ID: 1234,
  218. Method: "GET",
  219. Path: "/foo",
  220. },
  221. },
  222. {
  223. // value specified
  224. mustNewForm(
  225. t,
  226. "foo",
  227. url.Values{"value": []string{"some_value"}},
  228. ),
  229. etcdserverpb.Request{
  230. ID: 1234,
  231. Method: "PUT",
  232. Val: "some_value",
  233. Path: "/foo",
  234. },
  235. },
  236. {
  237. // prevIndex specified
  238. mustNewForm(
  239. t,
  240. "foo",
  241. url.Values{"prevIndex": []string{"98765"}},
  242. ),
  243. etcdserverpb.Request{
  244. ID: 1234,
  245. Method: "PUT",
  246. PrevIndex: 98765,
  247. Path: "/foo",
  248. },
  249. },
  250. {
  251. // recursive specified
  252. mustNewForm(
  253. t,
  254. "foo",
  255. url.Values{"recursive": []string{"true"}},
  256. ),
  257. etcdserverpb.Request{
  258. ID: 1234,
  259. Method: "PUT",
  260. Recursive: true,
  261. Path: "/foo",
  262. },
  263. },
  264. {
  265. // sorted specified
  266. mustNewForm(
  267. t,
  268. "foo",
  269. url.Values{"sorted": []string{"true"}},
  270. ),
  271. etcdserverpb.Request{
  272. ID: 1234,
  273. Method: "PUT",
  274. Sorted: true,
  275. Path: "/foo",
  276. },
  277. },
  278. {
  279. // wait specified
  280. mustNewRequest(t, "foo?wait=true"),
  281. etcdserverpb.Request{
  282. ID: 1234,
  283. Method: "GET",
  284. Wait: true,
  285. Path: "/foo",
  286. },
  287. },
  288. {
  289. // empty TTL specified
  290. mustNewRequest(t, "foo?ttl="),
  291. etcdserverpb.Request{
  292. ID: 1234,
  293. Method: "GET",
  294. Path: "/foo",
  295. Expiration: 0,
  296. },
  297. },
  298. {
  299. // dir specified
  300. mustNewRequest(t, "foo?dir=true"),
  301. etcdserverpb.Request{
  302. ID: 1234,
  303. Method: "GET",
  304. Dir: true,
  305. Path: "/foo",
  306. },
  307. },
  308. {
  309. // dir specified negatively
  310. mustNewRequest(t, "foo?dir=false"),
  311. etcdserverpb.Request{
  312. ID: 1234,
  313. Method: "GET",
  314. Dir: false,
  315. Path: "/foo",
  316. },
  317. },
  318. {
  319. // prevExist should be non-null if specified
  320. mustNewForm(
  321. t,
  322. "foo",
  323. url.Values{"prevExist": []string{"true"}},
  324. ),
  325. etcdserverpb.Request{
  326. ID: 1234,
  327. Method: "PUT",
  328. PrevExist: boolp(true),
  329. Path: "/foo",
  330. },
  331. },
  332. {
  333. // prevExist should be non-null if specified
  334. mustNewForm(
  335. t,
  336. "foo",
  337. url.Values{"prevExist": []string{"false"}},
  338. ),
  339. etcdserverpb.Request{
  340. ID: 1234,
  341. Method: "PUT",
  342. PrevExist: boolp(false),
  343. Path: "/foo",
  344. },
  345. },
  346. // mix various fields
  347. {
  348. mustNewForm(
  349. t,
  350. "foo",
  351. url.Values{
  352. "value": []string{"some value"},
  353. "prevExist": []string{"true"},
  354. "prevValue": []string{"previous value"},
  355. },
  356. ),
  357. etcdserverpb.Request{
  358. ID: 1234,
  359. Method: "PUT",
  360. PrevExist: boolp(true),
  361. PrevValue: "previous value",
  362. Val: "some value",
  363. Path: "/foo",
  364. },
  365. },
  366. // query parameters should be used if given
  367. {
  368. mustNewForm(
  369. t,
  370. "foo?prevValue=woof",
  371. url.Values{},
  372. ),
  373. etcdserverpb.Request{
  374. ID: 1234,
  375. Method: "PUT",
  376. PrevValue: "woof",
  377. Path: "/foo",
  378. },
  379. },
  380. // but form values should take precedence over query parameters
  381. {
  382. mustNewForm(
  383. t,
  384. "foo?prevValue=woof",
  385. url.Values{
  386. "prevValue": []string{"miaow"},
  387. },
  388. ),
  389. etcdserverpb.Request{
  390. ID: 1234,
  391. Method: "PUT",
  392. PrevValue: "miaow",
  393. Path: "/foo",
  394. },
  395. },
  396. }
  397. for i, tt := range tests {
  398. got, err := parseRequest(tt.in, 1234)
  399. if err != nil {
  400. t.Errorf("#%d: err = %v, want %v", i, err, nil)
  401. }
  402. if !reflect.DeepEqual(got, tt.w) {
  403. t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
  404. }
  405. }
  406. // Test TTL separately until we don't rely on the time module...
  407. now := time.Now().UnixNano()
  408. req := mustNewForm(t, "foo", url.Values{"ttl": []string{"100"}})
  409. got, err := parseRequest(req, 1234)
  410. if err != nil {
  411. t.Fatalf("err = %v, want nil", err)
  412. }
  413. if got.Expiration <= now {
  414. t.Fatalf("expiration = %v, wanted > %v", got.Expiration, now)
  415. }
  416. // ensure TTL=0 results in an expiration time
  417. req = mustNewForm(t, "foo", url.Values{"ttl": []string{"0"}})
  418. got, err = parseRequest(req, 1234)
  419. if err != nil {
  420. t.Fatalf("err = %v, want nil", err)
  421. }
  422. if got.Expiration <= now {
  423. t.Fatalf("expiration = %v, wanted > %v", got.Expiration, now)
  424. }
  425. }
  426. // eventingWatcher immediately returns a simple event of the given action on its channel
  427. type eventingWatcher struct {
  428. action string
  429. }
  430. func (w *eventingWatcher) EventChan() chan *store.Event {
  431. ch := make(chan *store.Event)
  432. go func() {
  433. ch <- &store.Event{
  434. Action: w.action,
  435. Node: &store.NodeExtern{},
  436. }
  437. }()
  438. return ch
  439. }
  440. func (w *eventingWatcher) Remove() {}
  441. func TestWriteError(t *testing.T) {
  442. // nil error should not panic
  443. rw := httptest.NewRecorder()
  444. writeError(rw, nil)
  445. h := rw.Header()
  446. if len(h) > 0 {
  447. t.Fatalf("unexpected non-empty headers: %#v", h)
  448. }
  449. b := rw.Body.String()
  450. if len(b) > 0 {
  451. t.Fatalf("unexpected non-empty body: %q", b)
  452. }
  453. tests := []struct {
  454. err error
  455. wcode int
  456. wi string
  457. }{
  458. {
  459. etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/foo/bar", 123),
  460. http.StatusNotFound,
  461. "123",
  462. },
  463. {
  464. etcdErr.NewError(etcdErr.EcodeTestFailed, "/foo/bar", 456),
  465. http.StatusPreconditionFailed,
  466. "456",
  467. },
  468. {
  469. err: errors.New("something went wrong"),
  470. wcode: http.StatusInternalServerError,
  471. },
  472. }
  473. for i, tt := range tests {
  474. rw := httptest.NewRecorder()
  475. writeError(rw, tt.err)
  476. if code := rw.Code; code != tt.wcode {
  477. t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
  478. }
  479. if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
  480. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
  481. }
  482. }
  483. }
  484. type dummyRaftTimer struct{}
  485. func (drt dummyRaftTimer) Index() uint64 { return uint64(100) }
  486. func (drt dummyRaftTimer) Term() uint64 { return uint64(5) }
  487. func TestWriteEvent(t *testing.T) {
  488. // nil event should not panic
  489. rw := httptest.NewRecorder()
  490. writeEvent(rw, nil, dummyRaftTimer{})
  491. h := rw.Header()
  492. if len(h) > 0 {
  493. t.Fatalf("unexpected non-empty headers: %#v", h)
  494. }
  495. b := rw.Body.String()
  496. if len(b) > 0 {
  497. t.Fatalf("unexpected non-empty body: %q", b)
  498. }
  499. tests := []struct {
  500. ev *store.Event
  501. idx string
  502. // TODO(jonboulle): check body as well as just status code
  503. code int
  504. err error
  505. }{
  506. // standard case, standard 200 response
  507. {
  508. &store.Event{
  509. Action: store.Get,
  510. Node: &store.NodeExtern{},
  511. PrevNode: &store.NodeExtern{},
  512. },
  513. "0",
  514. http.StatusOK,
  515. nil,
  516. },
  517. // check new nodes return StatusCreated
  518. {
  519. &store.Event{
  520. Action: store.Create,
  521. Node: &store.NodeExtern{},
  522. PrevNode: &store.NodeExtern{},
  523. },
  524. "0",
  525. http.StatusCreated,
  526. nil,
  527. },
  528. }
  529. for i, tt := range tests {
  530. rw := httptest.NewRecorder()
  531. writeEvent(rw, tt.ev, dummyRaftTimer{})
  532. if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
  533. t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
  534. }
  535. if gri := rw.Header().Get("X-Raft-Index"); gri != "100" {
  536. t.Errorf("case %d: bad X-Raft-Index header: got %s, want %s", i, gri, "100")
  537. }
  538. if grt := rw.Header().Get("X-Raft-Term"); grt != "5" {
  539. t.Errorf("case %d: bad X-Raft-Term header: got %s, want %s", i, grt, "5")
  540. }
  541. if gei := rw.Header().Get("X-Etcd-Index"); gei != tt.idx {
  542. t.Errorf("case %d: bad X-Etcd-Index header: got %s, want %s", i, gei, tt.idx)
  543. }
  544. if rw.Code != tt.code {
  545. t.Errorf("case %d: bad response code: got %d, want %v", i, rw.Code, tt.code)
  546. }
  547. }
  548. }
  549. type dummyWatcher struct {
  550. echan chan *store.Event
  551. sidx uint64
  552. }
  553. func (w *dummyWatcher) EventChan() chan *store.Event {
  554. return w.echan
  555. }
  556. func (w *dummyWatcher) StartIndex() uint64 { return w.sidx }
  557. func (w *dummyWatcher) Remove() {}
  558. func TestV2MembersEndpoint(t *testing.T) {
  559. tests := []struct {
  560. method string
  561. wcode int
  562. }{
  563. {"GET", http.StatusOK},
  564. {"HEAD", http.StatusOK},
  565. {"POST", http.StatusMethodNotAllowed},
  566. }
  567. m := NewClientHandler(&etcdserver.EtcdServer{ClusterStore: &fakeCluster{}})
  568. s := httptest.NewServer(m)
  569. defer s.Close()
  570. for _, tt := range tests {
  571. req, err := http.NewRequest(tt.method, s.URL+membersPrefix, nil)
  572. if err != nil {
  573. t.Fatal(err)
  574. }
  575. resp, err := http.DefaultClient.Do(req)
  576. if err != nil {
  577. t.Fatal(err)
  578. }
  579. if resp.StatusCode != tt.wcode {
  580. t.Errorf("StatusCode = %d, expected %d", resp.StatusCode, tt.wcode)
  581. }
  582. }
  583. }
  584. func TestServeMachines(t *testing.T) {
  585. cluster := &fakeCluster{
  586. members: []etcdserver.Member{
  587. {ID: 0xBEEF0, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}},
  588. {ID: 0xBEEF1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}},
  589. {ID: 0xBEEF2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8082"}}},
  590. },
  591. }
  592. writer := httptest.NewRecorder()
  593. req, err := http.NewRequest("GET", "", nil)
  594. if err != nil {
  595. t.Fatal(err)
  596. }
  597. h := &serverHandler{clusterStore: cluster}
  598. h.serveMachines(writer, req)
  599. w := "http://localhost:8080, http://localhost:8081, http://localhost:8082"
  600. if g := writer.Body.String(); g != w {
  601. t.Errorf("body = %s, want %s", g, w)
  602. }
  603. if writer.Code != http.StatusOK {
  604. t.Errorf("header = %d, want %d", writer.Code, http.StatusOK)
  605. }
  606. }
  607. func TestAllowMethod(t *testing.T) {
  608. tests := []struct {
  609. m string
  610. ms []string
  611. w bool
  612. wh string
  613. }{
  614. // Accepted methods
  615. {
  616. m: "GET",
  617. ms: []string{"GET", "POST", "PUT"},
  618. w: true,
  619. },
  620. {
  621. m: "POST",
  622. ms: []string{"POST"},
  623. w: true,
  624. },
  625. // Made-up methods no good
  626. {
  627. m: "FAKE",
  628. ms: []string{"GET", "POST", "PUT"},
  629. w: false,
  630. wh: "GET,POST,PUT",
  631. },
  632. // Empty methods no good
  633. {
  634. m: "",
  635. ms: []string{"GET", "POST"},
  636. w: false,
  637. wh: "GET,POST",
  638. },
  639. // Empty accepted methods no good
  640. {
  641. m: "GET",
  642. ms: []string{""},
  643. w: false,
  644. wh: "",
  645. },
  646. // No methods accepted
  647. {
  648. m: "GET",
  649. ms: []string{},
  650. w: false,
  651. wh: "",
  652. },
  653. }
  654. for i, tt := range tests {
  655. rw := httptest.NewRecorder()
  656. g := allowMethod(rw, tt.m, tt.ms...)
  657. if g != tt.w {
  658. t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
  659. }
  660. if !tt.w {
  661. if rw.Code != http.StatusMethodNotAllowed {
  662. t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
  663. }
  664. gh := rw.Header().Get("Allow")
  665. if gh != tt.wh {
  666. t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
  667. }
  668. }
  669. }
  670. }
  671. // errServer implements the etcd.Server interface for testing.
  672. // It returns the given error from any Do/Process/AddMember/RemoveMember calls.
  673. type errServer struct {
  674. err error
  675. }
  676. func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  677. return etcdserver.Response{}, fs.err
  678. }
  679. func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
  680. return fs.err
  681. }
  682. func (fs *errServer) Start() {}
  683. func (fs *errServer) Stop() {}
  684. func (fs *errServer) AddMember(ctx context.Context, m etcdserver.Member) error {
  685. return fs.err
  686. }
  687. func (fs *errServer) RemoveMember(ctx context.Context, id uint64) error {
  688. return fs.err
  689. }
  690. // errReader implements io.Reader to facilitate a broken request.
  691. type errReader struct{}
  692. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  693. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  694. json, err := m.Marshal()
  695. if err != nil {
  696. t.Fatalf("error marshalling raft Message: %#v", err)
  697. }
  698. return json
  699. }
  700. func TestServeRaft(t *testing.T) {
  701. testCases := []struct {
  702. method string
  703. body io.Reader
  704. serverErr error
  705. wcode int
  706. }{
  707. {
  708. // bad method
  709. "GET",
  710. bytes.NewReader(
  711. mustMarshalMsg(
  712. t,
  713. raftpb.Message{},
  714. ),
  715. ),
  716. nil,
  717. http.StatusMethodNotAllowed,
  718. },
  719. {
  720. // bad method
  721. "PUT",
  722. bytes.NewReader(
  723. mustMarshalMsg(
  724. t,
  725. raftpb.Message{},
  726. ),
  727. ),
  728. nil,
  729. http.StatusMethodNotAllowed,
  730. },
  731. {
  732. // bad method
  733. "DELETE",
  734. bytes.NewReader(
  735. mustMarshalMsg(
  736. t,
  737. raftpb.Message{},
  738. ),
  739. ),
  740. nil,
  741. http.StatusMethodNotAllowed,
  742. },
  743. {
  744. // bad request body
  745. "POST",
  746. &errReader{},
  747. nil,
  748. http.StatusBadRequest,
  749. },
  750. {
  751. // bad request protobuf
  752. "POST",
  753. strings.NewReader("malformed garbage"),
  754. nil,
  755. http.StatusBadRequest,
  756. },
  757. {
  758. // good request, etcdserver.Server error
  759. "POST",
  760. bytes.NewReader(
  761. mustMarshalMsg(
  762. t,
  763. raftpb.Message{},
  764. ),
  765. ),
  766. errors.New("some error"),
  767. http.StatusInternalServerError,
  768. },
  769. {
  770. // good request
  771. "POST",
  772. bytes.NewReader(
  773. mustMarshalMsg(
  774. t,
  775. raftpb.Message{},
  776. ),
  777. ),
  778. nil,
  779. http.StatusNoContent,
  780. },
  781. }
  782. for i, tt := range testCases {
  783. req, err := http.NewRequest(tt.method, "foo", tt.body)
  784. if err != nil {
  785. t.Fatalf("#%d: could not create request: %#v", i, err)
  786. }
  787. h := &serverHandler{
  788. timeout: time.Hour,
  789. server: &errServer{tt.serverErr},
  790. }
  791. rw := httptest.NewRecorder()
  792. h.serveRaft(rw, req)
  793. if rw.Code != tt.wcode {
  794. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  795. }
  796. }
  797. }
  798. // resServer implements the etcd.Server interface for testing.
  799. // It returns the given responsefrom any Do calls, and nil error
  800. type resServer struct {
  801. res etcdserver.Response
  802. }
  803. func (rs *resServer) Do(_ context.Context, _ etcdserverpb.Request) (etcdserver.Response, error) {
  804. return rs.res, nil
  805. }
  806. func (rs *resServer) Process(_ context.Context, _ raftpb.Message) error { return nil }
  807. func (rs *resServer) Start() {}
  808. func (rs *resServer) Stop() {}
  809. func (rs *resServer) AddMember(_ context.Context, _ etcdserver.Member) error { return nil }
  810. func (rs *resServer) RemoveMember(_ context.Context, _ uint64) error { return nil }
  811. func mustMarshalEvent(t *testing.T, ev *store.Event) string {
  812. b := new(bytes.Buffer)
  813. if err := json.NewEncoder(b).Encode(ev); err != nil {
  814. t.Fatalf("error marshalling event %#v: %v", ev, err)
  815. }
  816. return b.String()
  817. }
  818. func TestBadServeKeys(t *testing.T) {
  819. testBadCases := []struct {
  820. req *http.Request
  821. server etcdserver.Server
  822. wcode int
  823. }{
  824. {
  825. // bad method
  826. &http.Request{
  827. Method: "CONNECT",
  828. },
  829. &resServer{},
  830. http.StatusMethodNotAllowed,
  831. },
  832. {
  833. // bad method
  834. &http.Request{
  835. Method: "TRACE",
  836. },
  837. &resServer{},
  838. http.StatusMethodNotAllowed,
  839. },
  840. {
  841. // parseRequest error
  842. &http.Request{
  843. Body: nil,
  844. Method: "PUT",
  845. },
  846. &resServer{},
  847. http.StatusBadRequest,
  848. },
  849. {
  850. // etcdserver.Server error
  851. mustNewRequest(t, "foo"),
  852. &errServer{
  853. errors.New("blah"),
  854. },
  855. http.StatusInternalServerError,
  856. },
  857. {
  858. // non-event/watcher response from etcdserver.Server
  859. mustNewRequest(t, "foo"),
  860. &resServer{
  861. etcdserver.Response{},
  862. },
  863. http.StatusInternalServerError,
  864. },
  865. }
  866. for i, tt := range testBadCases {
  867. h := &serverHandler{
  868. timeout: 0, // context times out immediately
  869. server: tt.server,
  870. }
  871. rw := httptest.NewRecorder()
  872. h.serveKeys(rw, tt.req)
  873. if rw.Code != tt.wcode {
  874. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  875. }
  876. }
  877. }
  878. func TestServeKeysEvent(t *testing.T) {
  879. req := mustNewRequest(t, "foo")
  880. server := &resServer{
  881. etcdserver.Response{
  882. Event: &store.Event{
  883. Action: store.Get,
  884. Node: &store.NodeExtern{},
  885. },
  886. },
  887. }
  888. h := &serverHandler{
  889. timeout: time.Hour,
  890. server: server,
  891. timer: &dummyRaftTimer{},
  892. }
  893. rw := httptest.NewRecorder()
  894. h.serveKeys(rw, req)
  895. wcode := http.StatusOK
  896. wbody := mustMarshalEvent(
  897. t,
  898. &store.Event{
  899. Action: store.Get,
  900. Node: &store.NodeExtern{},
  901. },
  902. )
  903. if rw.Code != wcode {
  904. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  905. }
  906. g := rw.Body.String()
  907. if g != wbody {
  908. t.Errorf("got body=%#v, want %#v", g, wbody)
  909. }
  910. }
  911. func TestServeKeysWatch(t *testing.T) {
  912. req := mustNewRequest(t, "/foo/bar")
  913. ec := make(chan *store.Event)
  914. dw := &dummyWatcher{
  915. echan: ec,
  916. }
  917. server := &resServer{
  918. etcdserver.Response{
  919. Watcher: dw,
  920. },
  921. }
  922. h := &serverHandler{
  923. timeout: time.Hour,
  924. server: server,
  925. timer: &dummyRaftTimer{},
  926. }
  927. go func() {
  928. ec <- &store.Event{
  929. Action: store.Get,
  930. Node: &store.NodeExtern{},
  931. }
  932. }()
  933. rw := httptest.NewRecorder()
  934. h.serveKeys(rw, req)
  935. wcode := http.StatusOK
  936. wbody := mustMarshalEvent(
  937. t,
  938. &store.Event{
  939. Action: store.Get,
  940. Node: &store.NodeExtern{},
  941. },
  942. )
  943. if rw.Code != wcode {
  944. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  945. }
  946. g := rw.Body.String()
  947. if g != wbody {
  948. t.Errorf("got body=%#v, want %#v", g, wbody)
  949. }
  950. }
  951. type recordingCloseNotifier struct {
  952. *httptest.ResponseRecorder
  953. cn chan bool
  954. }
  955. func (rcn *recordingCloseNotifier) CloseNotify() <-chan bool {
  956. return rcn.cn
  957. }
  958. func TestHandleWatch(t *testing.T) {
  959. defaultRwRr := func() (http.ResponseWriter, *httptest.ResponseRecorder) {
  960. r := httptest.NewRecorder()
  961. return r, r
  962. }
  963. noopEv := func(chan *store.Event) {}
  964. tests := []struct {
  965. getCtx func() context.Context
  966. getRwRr func() (http.ResponseWriter, *httptest.ResponseRecorder)
  967. doToChan func(chan *store.Event)
  968. wbody string
  969. }{
  970. {
  971. // Normal case: one event
  972. context.Background,
  973. defaultRwRr,
  974. func(ch chan *store.Event) {
  975. ch <- &store.Event{
  976. Action: store.Get,
  977. Node: &store.NodeExtern{},
  978. }
  979. },
  980. mustMarshalEvent(
  981. t,
  982. &store.Event{
  983. Action: store.Get,
  984. Node: &store.NodeExtern{},
  985. },
  986. ),
  987. },
  988. {
  989. // Channel is closed, no event
  990. context.Background,
  991. defaultRwRr,
  992. func(ch chan *store.Event) {
  993. close(ch)
  994. },
  995. "",
  996. },
  997. {
  998. // Simulate a timed-out context
  999. func() context.Context {
  1000. ctx, cancel := context.WithCancel(context.Background())
  1001. cancel()
  1002. return ctx
  1003. },
  1004. defaultRwRr,
  1005. noopEv,
  1006. "",
  1007. },
  1008. {
  1009. // Close-notifying request
  1010. context.Background,
  1011. func() (http.ResponseWriter, *httptest.ResponseRecorder) {
  1012. rw := &recordingCloseNotifier{
  1013. ResponseRecorder: httptest.NewRecorder(),
  1014. cn: make(chan bool, 1),
  1015. }
  1016. rw.cn <- true
  1017. return rw, rw.ResponseRecorder
  1018. },
  1019. noopEv,
  1020. "",
  1021. },
  1022. }
  1023. for i, tt := range tests {
  1024. rw, rr := tt.getRwRr()
  1025. wa := &dummyWatcher{
  1026. echan: make(chan *store.Event, 1),
  1027. sidx: 10,
  1028. }
  1029. tt.doToChan(wa.echan)
  1030. handleWatch(tt.getCtx(), rw, wa, false, dummyRaftTimer{})
  1031. wcode := http.StatusOK
  1032. wct := "application/json"
  1033. wei := "10"
  1034. wri := "100"
  1035. wrt := "5"
  1036. if rr.Code != wcode {
  1037. t.Errorf("#%d: got code=%d, want %d", rr.Code, wcode)
  1038. }
  1039. h := rr.Header()
  1040. if ct := h.Get("Content-Type"); ct != wct {
  1041. t.Errorf("#%d: Content-Type=%q, want %q", i, ct, wct)
  1042. }
  1043. if ei := h.Get("X-Etcd-Index"); ei != wei {
  1044. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, ei, wei)
  1045. }
  1046. if ri := h.Get("X-Raft-Index"); ri != wri {
  1047. t.Errorf("#%d: X-Raft-Index=%q, want %q", i, ri, wri)
  1048. }
  1049. if rt := h.Get("X-Raft-Term"); rt != wrt {
  1050. t.Errorf("#%d: X-Raft-Term=%q, want %q", i, rt, wrt)
  1051. }
  1052. g := rr.Body.String()
  1053. if g != tt.wbody {
  1054. t.Errorf("#%d: got body=%#v, want %#v", i, g, tt.wbody)
  1055. }
  1056. }
  1057. }
  1058. // flushingRecorder provides a channel to allow users to block until the Recorder is Flushed()
  1059. type flushingRecorder struct {
  1060. *httptest.ResponseRecorder
  1061. ch chan struct{}
  1062. }
  1063. func (fr *flushingRecorder) Flush() {
  1064. fr.ResponseRecorder.Flush()
  1065. fr.ch <- struct{}{}
  1066. }
  1067. func TestHandleWatchStreaming(t *testing.T) {
  1068. rw := &flushingRecorder{
  1069. httptest.NewRecorder(),
  1070. make(chan struct{}, 1),
  1071. }
  1072. wa := &dummyWatcher{
  1073. echan: make(chan *store.Event),
  1074. }
  1075. // Launch the streaming handler in the background with a cancellable context
  1076. ctx, cancel := context.WithCancel(context.Background())
  1077. done := make(chan struct{})
  1078. go func() {
  1079. handleWatch(ctx, rw, wa, true, dummyRaftTimer{})
  1080. close(done)
  1081. }()
  1082. // Expect one Flush for the headers etc.
  1083. select {
  1084. case <-rw.ch:
  1085. case <-time.After(time.Second):
  1086. t.Fatalf("timed out waiting for flush")
  1087. }
  1088. // Expect headers but no body
  1089. wcode := http.StatusOK
  1090. wct := "application/json"
  1091. wbody := ""
  1092. if rw.Code != wcode {
  1093. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1094. }
  1095. h := rw.Header()
  1096. if ct := h.Get("Content-Type"); ct != wct {
  1097. t.Errorf("Content-Type=%q, want %q", ct, wct)
  1098. }
  1099. g := rw.Body.String()
  1100. if g != wbody {
  1101. t.Errorf("got body=%#v, want %#v", g, wbody)
  1102. }
  1103. // Now send the first event
  1104. select {
  1105. case wa.echan <- &store.Event{
  1106. Action: store.Get,
  1107. Node: &store.NodeExtern{},
  1108. }:
  1109. case <-time.After(time.Second):
  1110. t.Fatal("timed out waiting for send")
  1111. }
  1112. // Wait for it to be flushed...
  1113. select {
  1114. case <-rw.ch:
  1115. case <-time.After(time.Second):
  1116. t.Fatalf("timed out waiting for flush")
  1117. }
  1118. // And check the body is as expected
  1119. wbody = mustMarshalEvent(
  1120. t,
  1121. &store.Event{
  1122. Action: store.Get,
  1123. Node: &store.NodeExtern{},
  1124. },
  1125. )
  1126. g = rw.Body.String()
  1127. if g != wbody {
  1128. t.Errorf("got body=%#v, want %#v", g, wbody)
  1129. }
  1130. // Rinse and repeat
  1131. select {
  1132. case wa.echan <- &store.Event{
  1133. Action: store.Get,
  1134. Node: &store.NodeExtern{},
  1135. }:
  1136. case <-time.After(time.Second):
  1137. t.Fatal("timed out waiting for send")
  1138. }
  1139. select {
  1140. case <-rw.ch:
  1141. case <-time.After(time.Second):
  1142. t.Fatalf("timed out waiting for flush")
  1143. }
  1144. // This time, we expect to see both events
  1145. wbody = wbody + wbody
  1146. g = rw.Body.String()
  1147. if g != wbody {
  1148. t.Errorf("got body=%#v, want %#v", g, wbody)
  1149. }
  1150. // Finally, time out the connection and ensure the serving goroutine returns
  1151. cancel()
  1152. select {
  1153. case <-done:
  1154. case <-time.After(time.Second):
  1155. t.Fatalf("timed out waiting for done")
  1156. }
  1157. }
  1158. func TestServeAdminMembersFail(t *testing.T) {
  1159. tests := []struct {
  1160. req *http.Request
  1161. server etcdserver.Server
  1162. wcode int
  1163. }{
  1164. {
  1165. // bad method
  1166. &http.Request{
  1167. Method: "CONNECT",
  1168. },
  1169. &resServer{},
  1170. http.StatusMethodNotAllowed,
  1171. },
  1172. {
  1173. // bad method
  1174. &http.Request{
  1175. Method: "TRACE",
  1176. },
  1177. &resServer{},
  1178. http.StatusMethodNotAllowed,
  1179. },
  1180. {
  1181. // parse id error
  1182. &http.Request{
  1183. URL: mustNewURL(t, path.Join(adminMembersPrefix, "badID")),
  1184. Method: "PUT",
  1185. },
  1186. &resServer{},
  1187. http.StatusBadRequest,
  1188. },
  1189. {
  1190. // parse body error
  1191. &http.Request{
  1192. URL: mustNewURL(t, path.Join(adminMembersPrefix, "1")),
  1193. Method: "PUT",
  1194. },
  1195. &resServer{},
  1196. http.StatusBadRequest,
  1197. },
  1198. {
  1199. // etcdserver.AddMember error
  1200. &http.Request{
  1201. URL: mustNewURL(t, path.Join(adminMembersPrefix, "1")),
  1202. Method: "PUT",
  1203. Body: ioutil.NopCloser(strings.NewReader("")),
  1204. },
  1205. &errServer{
  1206. errors.New("blah"),
  1207. },
  1208. http.StatusInternalServerError,
  1209. },
  1210. {
  1211. // etcdserver.RemoveMember error
  1212. &http.Request{
  1213. URL: mustNewURL(t, path.Join(adminMembersPrefix, "1")),
  1214. Method: "DELETE",
  1215. },
  1216. &errServer{
  1217. errors.New("blah"),
  1218. },
  1219. http.StatusInternalServerError,
  1220. },
  1221. }
  1222. for i, tt := range tests {
  1223. h := &serverHandler{
  1224. server: tt.server,
  1225. }
  1226. rw := httptest.NewRecorder()
  1227. h.serveAdminMembers(rw, tt.req)
  1228. if rw.Code != tt.wcode {
  1229. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  1230. }
  1231. }
  1232. }
  1233. type action struct {
  1234. name string
  1235. params []interface{}
  1236. }
  1237. type serverRecorder struct {
  1238. actions []action
  1239. }
  1240. func (s *serverRecorder) Do(_ context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  1241. s.actions = append(s.actions, action{name: "Do", params: []interface{}{r}})
  1242. return etcdserver.Response{}, nil
  1243. }
  1244. func (s *serverRecorder) Process(_ context.Context, m raftpb.Message) error {
  1245. s.actions = append(s.actions, action{name: "Process", params: []interface{}{m}})
  1246. return nil
  1247. }
  1248. func (s *serverRecorder) Start() {}
  1249. func (s *serverRecorder) Stop() {}
  1250. func (s *serverRecorder) AddMember(_ context.Context, m etcdserver.Member) error {
  1251. s.actions = append(s.actions, action{name: "AddMember", params: []interface{}{m}})
  1252. return nil
  1253. }
  1254. func (s *serverRecorder) RemoveMember(_ context.Context, id uint64) error {
  1255. s.actions = append(s.actions, action{name: "RemoveMember", params: []interface{}{id}})
  1256. return nil
  1257. }
  1258. func TestServeAdminMembersPut(t *testing.T) {
  1259. u := mustNewURL(t, path.Join(adminMembersPrefix, "BEEF"))
  1260. form := url.Values{"PeerURLs": []string{"http://a", "http://b"}}
  1261. body := strings.NewReader(form.Encode())
  1262. req, err := http.NewRequest("PUT", u.String(), body)
  1263. if err != nil {
  1264. t.Fatal(err)
  1265. }
  1266. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  1267. s := &serverRecorder{}
  1268. h := &serverHandler{
  1269. server: s,
  1270. }
  1271. rw := httptest.NewRecorder()
  1272. h.serveAdminMembers(rw, req)
  1273. wcode := http.StatusCreated
  1274. if rw.Code != wcode {
  1275. t.Errorf("code=%d, want %d", rw.Code, wcode)
  1276. }
  1277. g := rw.Body.String()
  1278. if g != "" {
  1279. t.Errorf("got body=%q, want %q", g, "")
  1280. }
  1281. wm := etcdserver.Member{
  1282. ID: 0xBEEF,
  1283. RaftAttributes: etcdserver.RaftAttributes{
  1284. PeerURLs: []string{"http://a", "http://b"},
  1285. },
  1286. }
  1287. wactions := []action{{name: "AddMember", params: []interface{}{wm}}}
  1288. if !reflect.DeepEqual(s.actions, wactions) {
  1289. t.Errorf("actions = %+v, want %+v", s.actions, wactions)
  1290. }
  1291. }
  1292. func TestServeAdminMembersDelete(t *testing.T) {
  1293. req := &http.Request{
  1294. Method: "DELETE",
  1295. URL: mustNewURL(t, path.Join(adminMembersPrefix, "BEEF")),
  1296. }
  1297. s := &serverRecorder{}
  1298. h := &serverHandler{
  1299. server: s,
  1300. }
  1301. rw := httptest.NewRecorder()
  1302. h.serveAdminMembers(rw, req)
  1303. wcode := http.StatusNoContent
  1304. if rw.Code != wcode {
  1305. t.Errorf("code=%d, want %d", rw.Code, wcode)
  1306. }
  1307. g := rw.Body.String()
  1308. if g != "" {
  1309. t.Errorf("got body=%q, want %q", g, "")
  1310. }
  1311. wactions := []action{{name: "RemoveMember", params: []interface{}{uint64(0xBEEF)}}}
  1312. if !reflect.DeepEqual(s.actions, wactions) {
  1313. t.Errorf("actions = %+v, want %+v", s.actions, wactions)
  1314. }
  1315. }
  1316. type fakeCluster struct {
  1317. members []etcdserver.Member
  1318. }
  1319. func (c *fakeCluster) Add(m etcdserver.Member) { return }
  1320. func (c *fakeCluster) Get() etcdserver.Cluster {
  1321. cl := &etcdserver.Cluster{}
  1322. cl.AddSlice(c.members)
  1323. return *cl
  1324. }
  1325. func (c *fakeCluster) Remove(id uint64) { return }