http_test.go 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913
  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. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "net/http"
  22. "net/http/httptest"
  23. "net/url"
  24. "path"
  25. "reflect"
  26. "sort"
  27. "strconv"
  28. "strings"
  29. "testing"
  30. "time"
  31. "github.com/coreos/etcd/Godeps/_workspace/src/code.google.com/p/go.net/context"
  32. "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
  33. etcdErr "github.com/coreos/etcd/error"
  34. "github.com/coreos/etcd/etcdserver"
  35. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  36. "github.com/coreos/etcd/raft/raftpb"
  37. "github.com/coreos/etcd/store"
  38. "github.com/coreos/etcd/version"
  39. )
  40. func boolp(b bool) *bool { return &b }
  41. func mustNewURL(t *testing.T, s string) *url.URL {
  42. u, err := url.Parse(s)
  43. if err != nil {
  44. t.Fatalf("error creating URL from %q: %v", s, err)
  45. }
  46. return u
  47. }
  48. // mustNewRequest takes a path, appends it to the standard keysPrefix, and constructs
  49. // a GET *http.Request referencing the resulting URL
  50. func mustNewRequest(t *testing.T, p string) *http.Request {
  51. return mustNewMethodRequest(t, "GET", p)
  52. }
  53. func mustNewMethodRequest(t *testing.T, m, p string) *http.Request {
  54. return &http.Request{
  55. Method: m,
  56. URL: mustNewURL(t, path.Join(keysPrefix, p)),
  57. }
  58. }
  59. // mustNewForm takes a set of Values and constructs a PUT *http.Request,
  60. // with a URL constructed from appending the given path to the standard keysPrefix
  61. func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
  62. u := mustNewURL(t, path.Join(keysPrefix, p))
  63. req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
  64. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  65. if err != nil {
  66. t.Fatalf("error creating new request: %v", err)
  67. }
  68. return req
  69. }
  70. func TestBadParseRequest(t *testing.T) {
  71. tests := []struct {
  72. in *http.Request
  73. wcode int
  74. }{
  75. {
  76. // parseForm failure
  77. &http.Request{
  78. Body: nil,
  79. Method: "PUT",
  80. },
  81. etcdErr.EcodeInvalidForm,
  82. },
  83. {
  84. // bad key prefix
  85. &http.Request{
  86. URL: mustNewURL(t, "/badprefix/"),
  87. },
  88. etcdErr.EcodeInvalidForm,
  89. },
  90. // bad values for prevIndex, waitIndex, ttl
  91. {
  92. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"garbage"}}),
  93. etcdErr.EcodeIndexNaN,
  94. },
  95. {
  96. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"1.5"}}),
  97. etcdErr.EcodeIndexNaN,
  98. },
  99. {
  100. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"-1"}}),
  101. etcdErr.EcodeIndexNaN,
  102. },
  103. {
  104. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"garbage"}}),
  105. etcdErr.EcodeIndexNaN,
  106. },
  107. {
  108. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"??"}}),
  109. etcdErr.EcodeIndexNaN,
  110. },
  111. {
  112. mustNewForm(t, "foo", url.Values{"ttl": []string{"-1"}}),
  113. etcdErr.EcodeTTLNaN,
  114. },
  115. // bad values for recursive, sorted, wait, prevExist, dir, stream
  116. {
  117. mustNewForm(t, "foo", url.Values{"recursive": []string{"hahaha"}}),
  118. etcdErr.EcodeInvalidField,
  119. },
  120. {
  121. mustNewForm(t, "foo", url.Values{"recursive": []string{"1234"}}),
  122. etcdErr.EcodeInvalidField,
  123. },
  124. {
  125. mustNewForm(t, "foo", url.Values{"recursive": []string{"?"}}),
  126. etcdErr.EcodeInvalidField,
  127. },
  128. {
  129. mustNewForm(t, "foo", url.Values{"sorted": []string{"?"}}),
  130. etcdErr.EcodeInvalidField,
  131. },
  132. {
  133. mustNewForm(t, "foo", url.Values{"sorted": []string{"x"}}),
  134. etcdErr.EcodeInvalidField,
  135. },
  136. {
  137. mustNewForm(t, "foo", url.Values{"wait": []string{"?!"}}),
  138. etcdErr.EcodeInvalidField,
  139. },
  140. {
  141. mustNewForm(t, "foo", url.Values{"wait": []string{"yes"}}),
  142. etcdErr.EcodeInvalidField,
  143. },
  144. {
  145. mustNewForm(t, "foo", url.Values{"prevExist": []string{"yes"}}),
  146. etcdErr.EcodeInvalidField,
  147. },
  148. {
  149. mustNewForm(t, "foo", url.Values{"prevExist": []string{"#2"}}),
  150. etcdErr.EcodeInvalidField,
  151. },
  152. {
  153. mustNewForm(t, "foo", url.Values{"dir": []string{"no"}}),
  154. etcdErr.EcodeInvalidField,
  155. },
  156. {
  157. mustNewForm(t, "foo", url.Values{"dir": []string{"file"}}),
  158. etcdErr.EcodeInvalidField,
  159. },
  160. {
  161. mustNewForm(t, "foo", url.Values{"quorum": []string{"no"}}),
  162. etcdErr.EcodeInvalidField,
  163. },
  164. {
  165. mustNewForm(t, "foo", url.Values{"quorum": []string{"file"}}),
  166. etcdErr.EcodeInvalidField,
  167. },
  168. {
  169. mustNewForm(t, "foo", url.Values{"stream": []string{"zzz"}}),
  170. etcdErr.EcodeInvalidField,
  171. },
  172. {
  173. mustNewForm(t, "foo", url.Values{"stream": []string{"something"}}),
  174. etcdErr.EcodeInvalidField,
  175. },
  176. // prevValue cannot be empty
  177. {
  178. mustNewForm(t, "foo", url.Values{"prevValue": []string{""}}),
  179. etcdErr.EcodeInvalidField,
  180. },
  181. // wait is only valid with GET requests
  182. {
  183. mustNewMethodRequest(t, "HEAD", "foo?wait=true"),
  184. etcdErr.EcodeInvalidField,
  185. },
  186. // query values are considered
  187. {
  188. mustNewRequest(t, "foo?prevExist=wrong"),
  189. etcdErr.EcodeInvalidField,
  190. },
  191. {
  192. mustNewRequest(t, "foo?ttl=wrong"),
  193. etcdErr.EcodeTTLNaN,
  194. },
  195. // but body takes precedence if both are specified
  196. {
  197. mustNewForm(
  198. t,
  199. "foo?ttl=12",
  200. url.Values{"ttl": []string{"garbage"}},
  201. ),
  202. etcdErr.EcodeTTLNaN,
  203. },
  204. {
  205. mustNewForm(
  206. t,
  207. "foo?prevExist=false",
  208. url.Values{"prevExist": []string{"yes"}},
  209. ),
  210. etcdErr.EcodeInvalidField,
  211. },
  212. }
  213. for i, tt := range tests {
  214. got, err := parseKeyRequest(tt.in, 1234, clockwork.NewFakeClock())
  215. if err == nil {
  216. t.Errorf("#%d: unexpected nil error!", i)
  217. continue
  218. }
  219. ee, ok := err.(*etcdErr.Error)
  220. if !ok {
  221. t.Errorf("#%d: err is not etcd.Error!", i)
  222. continue
  223. }
  224. if ee.ErrorCode != tt.wcode {
  225. t.Errorf("#%d: code=%d, want %v", i, ee.ErrorCode, tt.wcode)
  226. t.Logf("cause: %#v", ee.Cause)
  227. }
  228. if !reflect.DeepEqual(got, etcdserverpb.Request{}) {
  229. t.Errorf("#%d: unexpected non-empty Request: %#v", i, got)
  230. }
  231. }
  232. }
  233. func TestGoodParseRequest(t *testing.T) {
  234. fc := clockwork.NewFakeClock()
  235. fc.Advance(1111)
  236. tests := []struct {
  237. in *http.Request
  238. w etcdserverpb.Request
  239. }{
  240. {
  241. // good prefix, all other values default
  242. mustNewRequest(t, "foo"),
  243. etcdserverpb.Request{
  244. ID: 1234,
  245. Method: "GET",
  246. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  247. },
  248. },
  249. {
  250. // value specified
  251. mustNewForm(
  252. t,
  253. "foo",
  254. url.Values{"value": []string{"some_value"}},
  255. ),
  256. etcdserverpb.Request{
  257. ID: 1234,
  258. Method: "PUT",
  259. Val: "some_value",
  260. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  261. },
  262. },
  263. {
  264. // prevIndex specified
  265. mustNewForm(
  266. t,
  267. "foo",
  268. url.Values{"prevIndex": []string{"98765"}},
  269. ),
  270. etcdserverpb.Request{
  271. ID: 1234,
  272. Method: "PUT",
  273. PrevIndex: 98765,
  274. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  275. },
  276. },
  277. {
  278. // recursive specified
  279. mustNewForm(
  280. t,
  281. "foo",
  282. url.Values{"recursive": []string{"true"}},
  283. ),
  284. etcdserverpb.Request{
  285. ID: 1234,
  286. Method: "PUT",
  287. Recursive: true,
  288. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  289. },
  290. },
  291. {
  292. // sorted specified
  293. mustNewForm(
  294. t,
  295. "foo",
  296. url.Values{"sorted": []string{"true"}},
  297. ),
  298. etcdserverpb.Request{
  299. ID: 1234,
  300. Method: "PUT",
  301. Sorted: true,
  302. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  303. },
  304. },
  305. {
  306. // quorum specified
  307. mustNewForm(
  308. t,
  309. "foo",
  310. url.Values{"quorum": []string{"true"}},
  311. ),
  312. etcdserverpb.Request{
  313. ID: 1234,
  314. Method: "PUT",
  315. Quorum: true,
  316. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  317. },
  318. },
  319. {
  320. // wait specified
  321. mustNewRequest(t, "foo?wait=true"),
  322. etcdserverpb.Request{
  323. ID: 1234,
  324. Method: "GET",
  325. Wait: true,
  326. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  327. },
  328. },
  329. {
  330. // empty TTL specified
  331. mustNewRequest(t, "foo?ttl="),
  332. etcdserverpb.Request{
  333. ID: 1234,
  334. Method: "GET",
  335. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  336. Expiration: 0,
  337. },
  338. },
  339. {
  340. // non-empty TTL specified
  341. mustNewRequest(t, "foo?ttl=5678"),
  342. etcdserverpb.Request{
  343. ID: 1234,
  344. Method: "GET",
  345. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  346. Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
  347. },
  348. },
  349. {
  350. // zero TTL specified
  351. mustNewRequest(t, "foo?ttl=0"),
  352. etcdserverpb.Request{
  353. ID: 1234,
  354. Method: "GET",
  355. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  356. Expiration: fc.Now().UnixNano(),
  357. },
  358. },
  359. {
  360. // dir specified
  361. mustNewRequest(t, "foo?dir=true"),
  362. etcdserverpb.Request{
  363. ID: 1234,
  364. Method: "GET",
  365. Dir: true,
  366. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  367. },
  368. },
  369. {
  370. // dir specified negatively
  371. mustNewRequest(t, "foo?dir=false"),
  372. etcdserverpb.Request{
  373. ID: 1234,
  374. Method: "GET",
  375. Dir: false,
  376. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  377. },
  378. },
  379. {
  380. // prevExist should be non-null if specified
  381. mustNewForm(
  382. t,
  383. "foo",
  384. url.Values{"prevExist": []string{"true"}},
  385. ),
  386. etcdserverpb.Request{
  387. ID: 1234,
  388. Method: "PUT",
  389. PrevExist: boolp(true),
  390. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  391. },
  392. },
  393. {
  394. // prevExist should be non-null if specified
  395. mustNewForm(
  396. t,
  397. "foo",
  398. url.Values{"prevExist": []string{"false"}},
  399. ),
  400. etcdserverpb.Request{
  401. ID: 1234,
  402. Method: "PUT",
  403. PrevExist: boolp(false),
  404. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  405. },
  406. },
  407. // mix various fields
  408. {
  409. mustNewForm(
  410. t,
  411. "foo",
  412. url.Values{
  413. "value": []string{"some value"},
  414. "prevExist": []string{"true"},
  415. "prevValue": []string{"previous value"},
  416. },
  417. ),
  418. etcdserverpb.Request{
  419. ID: 1234,
  420. Method: "PUT",
  421. PrevExist: boolp(true),
  422. PrevValue: "previous value",
  423. Val: "some value",
  424. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  425. },
  426. },
  427. // query parameters should be used if given
  428. {
  429. mustNewForm(
  430. t,
  431. "foo?prevValue=woof",
  432. url.Values{},
  433. ),
  434. etcdserverpb.Request{
  435. ID: 1234,
  436. Method: "PUT",
  437. PrevValue: "woof",
  438. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  439. },
  440. },
  441. // but form values should take precedence over query parameters
  442. {
  443. mustNewForm(
  444. t,
  445. "foo?prevValue=woof",
  446. url.Values{
  447. "prevValue": []string{"miaow"},
  448. },
  449. ),
  450. etcdserverpb.Request{
  451. ID: 1234,
  452. Method: "PUT",
  453. PrevValue: "miaow",
  454. Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
  455. },
  456. },
  457. }
  458. for i, tt := range tests {
  459. got, err := parseKeyRequest(tt.in, 1234, fc)
  460. if err != nil {
  461. t.Errorf("#%d: err = %v, want %v", i, err, nil)
  462. }
  463. if !reflect.DeepEqual(got, tt.w) {
  464. t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
  465. }
  466. }
  467. }
  468. // eventingWatcher immediately returns a simple event of the given action on its channel
  469. type eventingWatcher struct {
  470. action string
  471. }
  472. func (w *eventingWatcher) EventChan() chan *store.Event {
  473. ch := make(chan *store.Event)
  474. go func() {
  475. ch <- &store.Event{
  476. Action: w.action,
  477. Node: &store.NodeExtern{},
  478. }
  479. }()
  480. return ch
  481. }
  482. func (w *eventingWatcher) Remove() {}
  483. func TestWriteError(t *testing.T) {
  484. // nil error should not panic
  485. rw := httptest.NewRecorder()
  486. writeError(rw, nil)
  487. h := rw.Header()
  488. if len(h) > 0 {
  489. t.Fatalf("unexpected non-empty headers: %#v", h)
  490. }
  491. b := rw.Body.String()
  492. if len(b) > 0 {
  493. t.Fatalf("unexpected non-empty body: %q", b)
  494. }
  495. tests := []struct {
  496. err error
  497. wcode int
  498. wi string
  499. }{
  500. {
  501. etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/foo/bar", 123),
  502. http.StatusNotFound,
  503. "123",
  504. },
  505. {
  506. etcdErr.NewError(etcdErr.EcodeTestFailed, "/foo/bar", 456),
  507. http.StatusPreconditionFailed,
  508. "456",
  509. },
  510. {
  511. err: errors.New("something went wrong"),
  512. wcode: http.StatusInternalServerError,
  513. },
  514. }
  515. for i, tt := range tests {
  516. rw := httptest.NewRecorder()
  517. writeError(rw, tt.err)
  518. if code := rw.Code; code != tt.wcode {
  519. t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
  520. }
  521. if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
  522. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
  523. }
  524. }
  525. }
  526. type dummyRaftTimer struct{}
  527. func (drt dummyRaftTimer) Index() uint64 { return uint64(100) }
  528. func (drt dummyRaftTimer) Term() uint64 { return uint64(5) }
  529. func TestWriteEvent(t *testing.T) {
  530. // nil event should not panic
  531. rw := httptest.NewRecorder()
  532. writeKeyEvent(rw, nil, dummyRaftTimer{})
  533. h := rw.Header()
  534. if len(h) > 0 {
  535. t.Fatalf("unexpected non-empty headers: %#v", h)
  536. }
  537. b := rw.Body.String()
  538. if len(b) > 0 {
  539. t.Fatalf("unexpected non-empty body: %q", b)
  540. }
  541. tests := []struct {
  542. ev *store.Event
  543. idx string
  544. // TODO(jonboulle): check body as well as just status code
  545. code int
  546. err error
  547. }{
  548. // standard case, standard 200 response
  549. {
  550. &store.Event{
  551. Action: store.Get,
  552. Node: &store.NodeExtern{},
  553. PrevNode: &store.NodeExtern{},
  554. },
  555. "0",
  556. http.StatusOK,
  557. nil,
  558. },
  559. // check new nodes return StatusCreated
  560. {
  561. &store.Event{
  562. Action: store.Create,
  563. Node: &store.NodeExtern{},
  564. PrevNode: &store.NodeExtern{},
  565. },
  566. "0",
  567. http.StatusCreated,
  568. nil,
  569. },
  570. }
  571. for i, tt := range tests {
  572. rw := httptest.NewRecorder()
  573. writeKeyEvent(rw, tt.ev, dummyRaftTimer{})
  574. if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
  575. t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
  576. }
  577. if gri := rw.Header().Get("X-Raft-Index"); gri != "100" {
  578. t.Errorf("case %d: bad X-Raft-Index header: got %s, want %s", i, gri, "100")
  579. }
  580. if grt := rw.Header().Get("X-Raft-Term"); grt != "5" {
  581. t.Errorf("case %d: bad X-Raft-Term header: got %s, want %s", i, grt, "5")
  582. }
  583. if gei := rw.Header().Get("X-Etcd-Index"); gei != tt.idx {
  584. t.Errorf("case %d: bad X-Etcd-Index header: got %s, want %s", i, gei, tt.idx)
  585. }
  586. if rw.Code != tt.code {
  587. t.Errorf("case %d: bad response code: got %d, want %v", i, rw.Code, tt.code)
  588. }
  589. }
  590. }
  591. type dummyWatcher struct {
  592. echan chan *store.Event
  593. sidx uint64
  594. }
  595. func (w *dummyWatcher) EventChan() chan *store.Event {
  596. return w.echan
  597. }
  598. func (w *dummyWatcher) StartIndex() uint64 { return w.sidx }
  599. func (w *dummyWatcher) Remove() {}
  600. func TestV2DeprecatedMachinesEndpoint(t *testing.T) {
  601. tests := []struct {
  602. method string
  603. wcode int
  604. }{
  605. {"GET", http.StatusOK},
  606. {"HEAD", http.StatusOK},
  607. {"POST", http.StatusMethodNotAllowed},
  608. }
  609. m := NewClientHandler(&etcdserver.EtcdServer{Cluster: &etcdserver.Cluster{}})
  610. s := httptest.NewServer(m)
  611. defer s.Close()
  612. for _, tt := range tests {
  613. req, err := http.NewRequest(tt.method, s.URL+deprecatedMachinesPrefix, nil)
  614. if err != nil {
  615. t.Fatal(err)
  616. }
  617. resp, err := http.DefaultClient.Do(req)
  618. if err != nil {
  619. t.Fatal(err)
  620. }
  621. if resp.StatusCode != tt.wcode {
  622. t.Errorf("StatusCode = %d, expected %d", resp.StatusCode, tt.wcode)
  623. }
  624. }
  625. }
  626. func TestServeMachines(t *testing.T) {
  627. cluster := &fakeCluster{
  628. clientURLs: []string{"http://localhost:8080", "http://localhost:8081", "http://localhost:8082"},
  629. }
  630. writer := httptest.NewRecorder()
  631. req, err := http.NewRequest("GET", "", nil)
  632. if err != nil {
  633. t.Fatal(err)
  634. }
  635. h := &serverHandler{clusterInfo: cluster}
  636. h.serveMachines(writer, req)
  637. w := "http://localhost:8080, http://localhost:8081, http://localhost:8082"
  638. if g := writer.Body.String(); g != w {
  639. t.Errorf("body = %s, want %s", g, w)
  640. }
  641. if writer.Code != http.StatusOK {
  642. t.Errorf("code = %d, want %d", writer.Code, http.StatusOK)
  643. }
  644. }
  645. type dummyStats struct {
  646. data []byte
  647. }
  648. func (ds *dummyStats) SelfStats() []byte { return ds.data }
  649. func (ds *dummyStats) LeaderStats() []byte { return ds.data }
  650. func (ds *dummyStats) StoreStats() []byte { return ds.data }
  651. func (ds *dummyStats) UpdateRecvApp(_ uint64, _ int64) {}
  652. func TestServeSelfStats(t *testing.T) {
  653. wb := []byte("some statistics")
  654. w := string(wb)
  655. sh := &serverHandler{
  656. stats: &dummyStats{data: wb},
  657. }
  658. rw := httptest.NewRecorder()
  659. sh.serveSelfStats(rw, &http.Request{Method: "GET"})
  660. if rw.Code != http.StatusOK {
  661. t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
  662. }
  663. wct := "application/json"
  664. if gct := rw.Header().Get("Content-Type"); gct != wct {
  665. t.Errorf("Content-Type = %q, want %q", gct, wct)
  666. }
  667. if g := rw.Body.String(); g != w {
  668. t.Errorf("body = %s, want %s", g, w)
  669. }
  670. }
  671. func TestSelfServeStatsBad(t *testing.T) {
  672. for _, m := range []string{"PUT", "POST", "DELETE"} {
  673. sh := &serverHandler{}
  674. rw := httptest.NewRecorder()
  675. sh.serveSelfStats(
  676. rw,
  677. &http.Request{
  678. Method: m,
  679. },
  680. )
  681. if rw.Code != http.StatusMethodNotAllowed {
  682. t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
  683. }
  684. }
  685. }
  686. func TestLeaderServeStatsBad(t *testing.T) {
  687. for _, m := range []string{"PUT", "POST", "DELETE"} {
  688. sh := &serverHandler{}
  689. rw := httptest.NewRecorder()
  690. sh.serveLeaderStats(
  691. rw,
  692. &http.Request{
  693. Method: m,
  694. },
  695. )
  696. if rw.Code != http.StatusMethodNotAllowed {
  697. t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
  698. }
  699. }
  700. }
  701. func TestServeLeaderStats(t *testing.T) {
  702. wb := []byte("some statistics")
  703. w := string(wb)
  704. sh := &serverHandler{
  705. stats: &dummyStats{data: wb},
  706. }
  707. rw := httptest.NewRecorder()
  708. sh.serveLeaderStats(rw, &http.Request{Method: "GET"})
  709. if rw.Code != http.StatusOK {
  710. t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
  711. }
  712. wct := "application/json"
  713. if gct := rw.Header().Get("Content-Type"); gct != wct {
  714. t.Errorf("Content-Type = %q, want %q", gct, wct)
  715. }
  716. if g := rw.Body.String(); g != w {
  717. t.Errorf("body = %s, want %s", g, w)
  718. }
  719. }
  720. func TestServeStoreStats(t *testing.T) {
  721. wb := []byte("some statistics")
  722. w := string(wb)
  723. sh := &serverHandler{
  724. stats: &dummyStats{data: wb},
  725. }
  726. rw := httptest.NewRecorder()
  727. sh.serveStoreStats(rw, &http.Request{Method: "GET"})
  728. if rw.Code != http.StatusOK {
  729. t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
  730. }
  731. wct := "application/json"
  732. if gct := rw.Header().Get("Content-Type"); gct != wct {
  733. t.Errorf("Content-Type = %q, want %q", gct, wct)
  734. }
  735. if g := rw.Body.String(); g != w {
  736. t.Errorf("body = %s, want %s", g, w)
  737. }
  738. }
  739. func TestServeVersion(t *testing.T) {
  740. req, err := http.NewRequest("GET", "", nil)
  741. if err != nil {
  742. t.Fatalf("error creating request: %v", err)
  743. }
  744. h := &serverHandler{}
  745. rw := httptest.NewRecorder()
  746. h.serveVersion(rw, req)
  747. if rw.Code != http.StatusOK {
  748. t.Errorf("code=%d, want %d", rw.Code, http.StatusOK)
  749. }
  750. w := fmt.Sprintf("etcd %s", version.Version)
  751. if g := rw.Body.String(); g != w {
  752. t.Fatalf("body = %q, want %q", g, w)
  753. }
  754. }
  755. func TestServeVersionFails(t *testing.T) {
  756. for _, m := range []string{
  757. "CONNECT", "TRACE", "PUT", "POST", "HEAD",
  758. } {
  759. req, err := http.NewRequest(m, "", nil)
  760. if err != nil {
  761. t.Fatalf("error creating request: %v", err)
  762. }
  763. h := &serverHandler{}
  764. rw := httptest.NewRecorder()
  765. h.serveVersion(rw, req)
  766. if rw.Code != http.StatusMethodNotAllowed {
  767. t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
  768. }
  769. }
  770. }
  771. func TestAllowMethod(t *testing.T) {
  772. tests := []struct {
  773. m string
  774. ms []string
  775. w bool
  776. wh string
  777. }{
  778. // Accepted methods
  779. {
  780. m: "GET",
  781. ms: []string{"GET", "POST", "PUT"},
  782. w: true,
  783. },
  784. {
  785. m: "POST",
  786. ms: []string{"POST"},
  787. w: true,
  788. },
  789. // Made-up methods no good
  790. {
  791. m: "FAKE",
  792. ms: []string{"GET", "POST", "PUT"},
  793. w: false,
  794. wh: "GET,POST,PUT",
  795. },
  796. // Empty methods no good
  797. {
  798. m: "",
  799. ms: []string{"GET", "POST"},
  800. w: false,
  801. wh: "GET,POST",
  802. },
  803. // Empty accepted methods no good
  804. {
  805. m: "GET",
  806. ms: []string{""},
  807. w: false,
  808. wh: "",
  809. },
  810. // No methods accepted
  811. {
  812. m: "GET",
  813. ms: []string{},
  814. w: false,
  815. wh: "",
  816. },
  817. }
  818. for i, tt := range tests {
  819. rw := httptest.NewRecorder()
  820. g := allowMethod(rw, tt.m, tt.ms...)
  821. if g != tt.w {
  822. t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
  823. }
  824. if !tt.w {
  825. if rw.Code != http.StatusMethodNotAllowed {
  826. t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
  827. }
  828. gh := rw.Header().Get("Allow")
  829. if gh != tt.wh {
  830. t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
  831. }
  832. }
  833. }
  834. }
  835. // errServer implements the etcd.Server interface for testing.
  836. // It returns the given error from any Do/Process/AddMember/RemoveMember calls.
  837. type errServer struct {
  838. err error
  839. }
  840. func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  841. return etcdserver.Response{}, fs.err
  842. }
  843. func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
  844. return fs.err
  845. }
  846. func (fs *errServer) Start() {}
  847. func (fs *errServer) Stop() {}
  848. func (fs *errServer) AddMember(ctx context.Context, m etcdserver.Member) error {
  849. return fs.err
  850. }
  851. func (fs *errServer) RemoveMember(ctx context.Context, id uint64) error {
  852. return fs.err
  853. }
  854. // errReader implements io.Reader to facilitate a broken request.
  855. type errReader struct{}
  856. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  857. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  858. json, err := m.Marshal()
  859. if err != nil {
  860. t.Fatalf("error marshalling raft Message: %#v", err)
  861. }
  862. return json
  863. }
  864. func TestServeRaft(t *testing.T) {
  865. testCases := []struct {
  866. method string
  867. body io.Reader
  868. serverErr error
  869. clusterID string
  870. wcode int
  871. }{
  872. {
  873. // bad method
  874. "GET",
  875. bytes.NewReader(
  876. mustMarshalMsg(
  877. t,
  878. raftpb.Message{},
  879. ),
  880. ),
  881. nil,
  882. "0",
  883. http.StatusMethodNotAllowed,
  884. },
  885. {
  886. // bad method
  887. "PUT",
  888. bytes.NewReader(
  889. mustMarshalMsg(
  890. t,
  891. raftpb.Message{},
  892. ),
  893. ),
  894. nil,
  895. "0",
  896. http.StatusMethodNotAllowed,
  897. },
  898. {
  899. // bad method
  900. "DELETE",
  901. bytes.NewReader(
  902. mustMarshalMsg(
  903. t,
  904. raftpb.Message{},
  905. ),
  906. ),
  907. nil,
  908. "0",
  909. http.StatusMethodNotAllowed,
  910. },
  911. {
  912. // bad request body
  913. "POST",
  914. &errReader{},
  915. nil,
  916. "0",
  917. http.StatusBadRequest,
  918. },
  919. {
  920. // bad request protobuf
  921. "POST",
  922. strings.NewReader("malformed garbage"),
  923. nil,
  924. "0",
  925. http.StatusBadRequest,
  926. },
  927. {
  928. // good request, etcdserver.Server internal error
  929. "POST",
  930. bytes.NewReader(
  931. mustMarshalMsg(
  932. t,
  933. raftpb.Message{},
  934. ),
  935. ),
  936. errors.New("some error"),
  937. "0",
  938. http.StatusInternalServerError,
  939. },
  940. {
  941. // good request from removed member
  942. "POST",
  943. bytes.NewReader(
  944. mustMarshalMsg(
  945. t,
  946. raftpb.Message{},
  947. ),
  948. ),
  949. etcdserver.ErrRemoved,
  950. "0",
  951. http.StatusForbidden,
  952. },
  953. {
  954. // good request
  955. "POST",
  956. bytes.NewReader(
  957. mustMarshalMsg(
  958. t,
  959. raftpb.Message{},
  960. ),
  961. ),
  962. nil,
  963. "1",
  964. http.StatusPreconditionFailed,
  965. },
  966. {
  967. // good request
  968. "POST",
  969. bytes.NewReader(
  970. mustMarshalMsg(
  971. t,
  972. raftpb.Message{},
  973. ),
  974. ),
  975. nil,
  976. "0",
  977. http.StatusNoContent,
  978. },
  979. }
  980. for i, tt := range testCases {
  981. req, err := http.NewRequest(tt.method, "foo", tt.body)
  982. if err != nil {
  983. t.Fatalf("#%d: could not create request: %#v", i, err)
  984. }
  985. req.Header.Set("X-Etcd-Cluster-ID", tt.clusterID)
  986. h := &serverHandler{
  987. timeout: time.Hour,
  988. server: &errServer{tt.serverErr},
  989. clusterInfo: &fakeCluster{id: 0},
  990. }
  991. rw := httptest.NewRecorder()
  992. h.serveRaft(rw, req)
  993. if rw.Code != tt.wcode {
  994. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  995. }
  996. }
  997. }
  998. func TestServeMembersFails(t *testing.T) {
  999. tests := []struct {
  1000. method string
  1001. wcode int
  1002. }{
  1003. {
  1004. "POST",
  1005. http.StatusMethodNotAllowed,
  1006. },
  1007. {
  1008. "DELETE",
  1009. http.StatusMethodNotAllowed,
  1010. },
  1011. {
  1012. "BAD",
  1013. http.StatusMethodNotAllowed,
  1014. },
  1015. }
  1016. for i, tt := range tests {
  1017. h := &serverHandler{}
  1018. rw := httptest.NewRecorder()
  1019. h.serveMembers(rw, &http.Request{Method: tt.method})
  1020. if rw.Code != tt.wcode {
  1021. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  1022. }
  1023. }
  1024. }
  1025. func TestServeMembersGet(t *testing.T) {
  1026. memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
  1027. memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
  1028. cluster := &fakeCluster{
  1029. id: 1,
  1030. members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
  1031. }
  1032. h := &serverHandler{
  1033. server: &serverRecorder{},
  1034. clusterInfo: cluster,
  1035. }
  1036. msb, err := json.Marshal([]etcdserver.Member{memb1, memb2})
  1037. if err != nil {
  1038. t.Fatal(err)
  1039. }
  1040. wms := string(msb) + "\n"
  1041. tests := []struct {
  1042. path string
  1043. wcode int
  1044. wct string
  1045. wbody string
  1046. }{
  1047. {membersPrefix, http.StatusOK, "application/json", wms},
  1048. {path.Join(membersPrefix, "bad"), http.StatusBadRequest, "text/plain; charset=utf-8", "bad path\n"},
  1049. }
  1050. for i, tt := range tests {
  1051. req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
  1052. if err != nil {
  1053. t.Fatal(err)
  1054. }
  1055. rw := httptest.NewRecorder()
  1056. h.serveMembers(rw, req)
  1057. if rw.Code != tt.wcode {
  1058. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  1059. }
  1060. if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
  1061. t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
  1062. }
  1063. if rw.Body.String() != tt.wbody {
  1064. t.Errorf("#%d: body = %s, want %s", i, rw.Body.String(), tt.wbody)
  1065. }
  1066. gcid := rw.Header().Get("X-Etcd-Cluster-ID")
  1067. wcid := strconv.FormatUint(cluster.ID(), 16)
  1068. if gcid != wcid {
  1069. t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
  1070. }
  1071. }
  1072. }
  1073. // resServer implements the etcd.Server interface for testing.
  1074. // It returns the given responsefrom any Do calls, and nil error
  1075. type resServer struct {
  1076. res etcdserver.Response
  1077. }
  1078. func (rs *resServer) Do(_ context.Context, _ etcdserverpb.Request) (etcdserver.Response, error) {
  1079. return rs.res, nil
  1080. }
  1081. func (rs *resServer) Process(_ context.Context, _ raftpb.Message) error { return nil }
  1082. func (rs *resServer) Start() {}
  1083. func (rs *resServer) Stop() {}
  1084. func (rs *resServer) AddMember(_ context.Context, _ etcdserver.Member) error { return nil }
  1085. func (rs *resServer) RemoveMember(_ context.Context, _ uint64) error { return nil }
  1086. func mustMarshalEvent(t *testing.T, ev *store.Event) string {
  1087. b := new(bytes.Buffer)
  1088. if err := json.NewEncoder(b).Encode(ev); err != nil {
  1089. t.Fatalf("error marshalling event %#v: %v", ev, err)
  1090. }
  1091. return b.String()
  1092. }
  1093. func TestBadServeKeys(t *testing.T) {
  1094. testBadCases := []struct {
  1095. req *http.Request
  1096. server etcdserver.Server
  1097. wcode int
  1098. wbody string
  1099. }{
  1100. {
  1101. // bad method
  1102. &http.Request{
  1103. Method: "CONNECT",
  1104. },
  1105. &resServer{},
  1106. http.StatusMethodNotAllowed,
  1107. "Method Not Allowed",
  1108. },
  1109. {
  1110. // bad method
  1111. &http.Request{
  1112. Method: "TRACE",
  1113. },
  1114. &resServer{},
  1115. http.StatusMethodNotAllowed,
  1116. "Method Not Allowed",
  1117. },
  1118. {
  1119. // parseRequest error
  1120. &http.Request{
  1121. Body: nil,
  1122. Method: "PUT",
  1123. },
  1124. &resServer{},
  1125. http.StatusBadRequest,
  1126. `{"errorCode":210,"message":"Invalid POST form","cause":"missing form body","index":0}`,
  1127. },
  1128. {
  1129. // etcdserver.Server error
  1130. mustNewRequest(t, "foo"),
  1131. &errServer{
  1132. errors.New("blah"),
  1133. },
  1134. http.StatusInternalServerError,
  1135. "Internal Server Error",
  1136. },
  1137. {
  1138. // etcdserver.Server etcd error
  1139. mustNewRequest(t, "foo"),
  1140. &errServer{
  1141. etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/1/pant", 0),
  1142. },
  1143. http.StatusNotFound,
  1144. `{"errorCode":100,"message":"Key not found","cause":"/pant","index":0}`,
  1145. },
  1146. {
  1147. // non-event/watcher response from etcdserver.Server
  1148. mustNewRequest(t, "foo"),
  1149. &resServer{
  1150. etcdserver.Response{},
  1151. },
  1152. http.StatusInternalServerError,
  1153. "Internal Server Error",
  1154. },
  1155. }
  1156. for i, tt := range testBadCases {
  1157. h := &serverHandler{
  1158. timeout: 0, // context times out immediately
  1159. server: tt.server,
  1160. }
  1161. rw := httptest.NewRecorder()
  1162. h.serveKeys(rw, tt.req)
  1163. if rw.Code != tt.wcode {
  1164. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  1165. }
  1166. if g := strings.TrimSuffix(rw.Body.String(), "\n"); g != tt.wbody {
  1167. t.Errorf("#%d: body = %s, want %s", i, g, tt.wbody)
  1168. }
  1169. }
  1170. }
  1171. func TestServeKeysEvent(t *testing.T) {
  1172. req := mustNewRequest(t, "foo")
  1173. server := &resServer{
  1174. etcdserver.Response{
  1175. Event: &store.Event{
  1176. Action: store.Get,
  1177. Node: &store.NodeExtern{},
  1178. },
  1179. },
  1180. }
  1181. h := &serverHandler{
  1182. timeout: time.Hour,
  1183. server: server,
  1184. timer: &dummyRaftTimer{},
  1185. }
  1186. rw := httptest.NewRecorder()
  1187. h.serveKeys(rw, req)
  1188. wcode := http.StatusOK
  1189. wbody := mustMarshalEvent(
  1190. t,
  1191. &store.Event{
  1192. Action: store.Get,
  1193. Node: &store.NodeExtern{},
  1194. },
  1195. )
  1196. if rw.Code != wcode {
  1197. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1198. }
  1199. g := rw.Body.String()
  1200. if g != wbody {
  1201. t.Errorf("got body=%#v, want %#v", g, wbody)
  1202. }
  1203. }
  1204. func TestServeKeysWatch(t *testing.T) {
  1205. req := mustNewRequest(t, "/foo/bar")
  1206. ec := make(chan *store.Event)
  1207. dw := &dummyWatcher{
  1208. echan: ec,
  1209. }
  1210. server := &resServer{
  1211. etcdserver.Response{
  1212. Watcher: dw,
  1213. },
  1214. }
  1215. h := &serverHandler{
  1216. timeout: time.Hour,
  1217. server: server,
  1218. timer: &dummyRaftTimer{},
  1219. }
  1220. go func() {
  1221. ec <- &store.Event{
  1222. Action: store.Get,
  1223. Node: &store.NodeExtern{},
  1224. }
  1225. }()
  1226. rw := httptest.NewRecorder()
  1227. h.serveKeys(rw, req)
  1228. wcode := http.StatusOK
  1229. wbody := mustMarshalEvent(
  1230. t,
  1231. &store.Event{
  1232. Action: store.Get,
  1233. Node: &store.NodeExtern{},
  1234. },
  1235. )
  1236. if rw.Code != wcode {
  1237. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1238. }
  1239. g := rw.Body.String()
  1240. if g != wbody {
  1241. t.Errorf("got body=%#v, want %#v", g, wbody)
  1242. }
  1243. }
  1244. type recordingCloseNotifier struct {
  1245. *httptest.ResponseRecorder
  1246. cn chan bool
  1247. }
  1248. func (rcn *recordingCloseNotifier) CloseNotify() <-chan bool {
  1249. return rcn.cn
  1250. }
  1251. func TestHandleWatch(t *testing.T) {
  1252. defaultRwRr := func() (http.ResponseWriter, *httptest.ResponseRecorder) {
  1253. r := httptest.NewRecorder()
  1254. return r, r
  1255. }
  1256. noopEv := func(chan *store.Event) {}
  1257. tests := []struct {
  1258. getCtx func() context.Context
  1259. getRwRr func() (http.ResponseWriter, *httptest.ResponseRecorder)
  1260. doToChan func(chan *store.Event)
  1261. wbody string
  1262. }{
  1263. {
  1264. // Normal case: one event
  1265. context.Background,
  1266. defaultRwRr,
  1267. func(ch chan *store.Event) {
  1268. ch <- &store.Event{
  1269. Action: store.Get,
  1270. Node: &store.NodeExtern{},
  1271. }
  1272. },
  1273. mustMarshalEvent(
  1274. t,
  1275. &store.Event{
  1276. Action: store.Get,
  1277. Node: &store.NodeExtern{},
  1278. },
  1279. ),
  1280. },
  1281. {
  1282. // Channel is closed, no event
  1283. context.Background,
  1284. defaultRwRr,
  1285. func(ch chan *store.Event) {
  1286. close(ch)
  1287. },
  1288. "",
  1289. },
  1290. {
  1291. // Simulate a timed-out context
  1292. func() context.Context {
  1293. ctx, cancel := context.WithCancel(context.Background())
  1294. cancel()
  1295. return ctx
  1296. },
  1297. defaultRwRr,
  1298. noopEv,
  1299. "",
  1300. },
  1301. {
  1302. // Close-notifying request
  1303. context.Background,
  1304. func() (http.ResponseWriter, *httptest.ResponseRecorder) {
  1305. rw := &recordingCloseNotifier{
  1306. ResponseRecorder: httptest.NewRecorder(),
  1307. cn: make(chan bool, 1),
  1308. }
  1309. rw.cn <- true
  1310. return rw, rw.ResponseRecorder
  1311. },
  1312. noopEv,
  1313. "",
  1314. },
  1315. }
  1316. for i, tt := range tests {
  1317. rw, rr := tt.getRwRr()
  1318. wa := &dummyWatcher{
  1319. echan: make(chan *store.Event, 1),
  1320. sidx: 10,
  1321. }
  1322. tt.doToChan(wa.echan)
  1323. handleKeyWatch(tt.getCtx(), rw, wa, false, dummyRaftTimer{})
  1324. wcode := http.StatusOK
  1325. wct := "application/json"
  1326. wei := "10"
  1327. wri := "100"
  1328. wrt := "5"
  1329. if rr.Code != wcode {
  1330. t.Errorf("#%d: got code=%d, want %d", i, rr.Code, wcode)
  1331. }
  1332. h := rr.Header()
  1333. if ct := h.Get("Content-Type"); ct != wct {
  1334. t.Errorf("#%d: Content-Type=%q, want %q", i, ct, wct)
  1335. }
  1336. if ei := h.Get("X-Etcd-Index"); ei != wei {
  1337. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, ei, wei)
  1338. }
  1339. if ri := h.Get("X-Raft-Index"); ri != wri {
  1340. t.Errorf("#%d: X-Raft-Index=%q, want %q", i, ri, wri)
  1341. }
  1342. if rt := h.Get("X-Raft-Term"); rt != wrt {
  1343. t.Errorf("#%d: X-Raft-Term=%q, want %q", i, rt, wrt)
  1344. }
  1345. g := rr.Body.String()
  1346. if g != tt.wbody {
  1347. t.Errorf("#%d: got body=%#v, want %#v", i, g, tt.wbody)
  1348. }
  1349. }
  1350. }
  1351. // flushingRecorder provides a channel to allow users to block until the Recorder is Flushed()
  1352. type flushingRecorder struct {
  1353. *httptest.ResponseRecorder
  1354. ch chan struct{}
  1355. }
  1356. func (fr *flushingRecorder) Flush() {
  1357. fr.ResponseRecorder.Flush()
  1358. fr.ch <- struct{}{}
  1359. }
  1360. func TestHandleWatchStreaming(t *testing.T) {
  1361. rw := &flushingRecorder{
  1362. httptest.NewRecorder(),
  1363. make(chan struct{}, 1),
  1364. }
  1365. wa := &dummyWatcher{
  1366. echan: make(chan *store.Event),
  1367. }
  1368. // Launch the streaming handler in the background with a cancellable context
  1369. ctx, cancel := context.WithCancel(context.Background())
  1370. done := make(chan struct{})
  1371. go func() {
  1372. handleKeyWatch(ctx, rw, wa, true, dummyRaftTimer{})
  1373. close(done)
  1374. }()
  1375. // Expect one Flush for the headers etc.
  1376. select {
  1377. case <-rw.ch:
  1378. case <-time.After(time.Second):
  1379. t.Fatalf("timed out waiting for flush")
  1380. }
  1381. // Expect headers but no body
  1382. wcode := http.StatusOK
  1383. wct := "application/json"
  1384. wbody := ""
  1385. if rw.Code != wcode {
  1386. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1387. }
  1388. h := rw.Header()
  1389. if ct := h.Get("Content-Type"); ct != wct {
  1390. t.Errorf("Content-Type=%q, want %q", ct, wct)
  1391. }
  1392. g := rw.Body.String()
  1393. if g != wbody {
  1394. t.Errorf("got body=%#v, want %#v", g, wbody)
  1395. }
  1396. // Now send the first event
  1397. select {
  1398. case wa.echan <- &store.Event{
  1399. Action: store.Get,
  1400. Node: &store.NodeExtern{},
  1401. }:
  1402. case <-time.After(time.Second):
  1403. t.Fatal("timed out waiting for send")
  1404. }
  1405. // Wait for it to be flushed...
  1406. select {
  1407. case <-rw.ch:
  1408. case <-time.After(time.Second):
  1409. t.Fatalf("timed out waiting for flush")
  1410. }
  1411. // And check the body is as expected
  1412. wbody = mustMarshalEvent(
  1413. t,
  1414. &store.Event{
  1415. Action: store.Get,
  1416. Node: &store.NodeExtern{},
  1417. },
  1418. )
  1419. g = rw.Body.String()
  1420. if g != wbody {
  1421. t.Errorf("got body=%#v, want %#v", g, wbody)
  1422. }
  1423. // Rinse and repeat
  1424. select {
  1425. case wa.echan <- &store.Event{
  1426. Action: store.Get,
  1427. Node: &store.NodeExtern{},
  1428. }:
  1429. case <-time.After(time.Second):
  1430. t.Fatal("timed out waiting for send")
  1431. }
  1432. select {
  1433. case <-rw.ch:
  1434. case <-time.After(time.Second):
  1435. t.Fatalf("timed out waiting for flush")
  1436. }
  1437. // This time, we expect to see both events
  1438. wbody = wbody + wbody
  1439. g = rw.Body.String()
  1440. if g != wbody {
  1441. t.Errorf("got body=%#v, want %#v", g, wbody)
  1442. }
  1443. // Finally, time out the connection and ensure the serving goroutine returns
  1444. cancel()
  1445. select {
  1446. case <-done:
  1447. case <-time.After(time.Second):
  1448. t.Fatalf("timed out waiting for done")
  1449. }
  1450. }
  1451. func TestServeAdminMembersFail(t *testing.T) {
  1452. tests := []struct {
  1453. req *http.Request
  1454. server etcdserver.Server
  1455. wcode int
  1456. }{
  1457. {
  1458. // bad method
  1459. &http.Request{
  1460. Method: "CONNECT",
  1461. },
  1462. &resServer{},
  1463. http.StatusMethodNotAllowed,
  1464. },
  1465. {
  1466. // bad method
  1467. &http.Request{
  1468. Method: "TRACE",
  1469. },
  1470. &resServer{},
  1471. http.StatusMethodNotAllowed,
  1472. },
  1473. {
  1474. // parse body error
  1475. &http.Request{
  1476. URL: mustNewURL(t, adminMembersPrefix),
  1477. Method: "POST",
  1478. Body: ioutil.NopCloser(strings.NewReader("bad json")),
  1479. },
  1480. &resServer{},
  1481. http.StatusBadRequest,
  1482. },
  1483. {
  1484. // bad content type
  1485. &http.Request{
  1486. URL: mustNewURL(t, adminMembersPrefix),
  1487. Method: "POST",
  1488. Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
  1489. Header: map[string][]string{"Content-Type": []string{"application/bad"}},
  1490. },
  1491. &errServer{},
  1492. http.StatusBadRequest,
  1493. },
  1494. {
  1495. // bad url
  1496. &http.Request{
  1497. URL: mustNewURL(t, adminMembersPrefix),
  1498. Method: "POST",
  1499. Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://a"]}`)),
  1500. Header: map[string][]string{"Content-Type": []string{"application/json"}},
  1501. },
  1502. &errServer{},
  1503. http.StatusBadRequest,
  1504. },
  1505. {
  1506. // etcdserver.AddMember error
  1507. &http.Request{
  1508. URL: mustNewURL(t, adminMembersPrefix),
  1509. Method: "POST",
  1510. Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
  1511. Header: map[string][]string{"Content-Type": []string{"application/json"}},
  1512. },
  1513. &errServer{
  1514. errors.New("blah"),
  1515. },
  1516. http.StatusInternalServerError,
  1517. },
  1518. {
  1519. // etcdserver.RemoveMember error
  1520. &http.Request{
  1521. URL: mustNewURL(t, path.Join(adminMembersPrefix, "1")),
  1522. Method: "DELETE",
  1523. },
  1524. &errServer{
  1525. errors.New("blah"),
  1526. },
  1527. http.StatusInternalServerError,
  1528. },
  1529. }
  1530. for i, tt := range tests {
  1531. h := &serverHandler{
  1532. server: tt.server,
  1533. clock: clockwork.NewFakeClock(),
  1534. }
  1535. rw := httptest.NewRecorder()
  1536. h.serveAdminMembers(rw, tt.req)
  1537. if rw.Code != tt.wcode {
  1538. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  1539. }
  1540. }
  1541. }
  1542. type action struct {
  1543. name string
  1544. params []interface{}
  1545. }
  1546. type serverRecorder struct {
  1547. actions []action
  1548. }
  1549. func (s *serverRecorder) Do(_ context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  1550. s.actions = append(s.actions, action{name: "Do", params: []interface{}{r}})
  1551. return etcdserver.Response{}, nil
  1552. }
  1553. func (s *serverRecorder) Process(_ context.Context, m raftpb.Message) error {
  1554. s.actions = append(s.actions, action{name: "Process", params: []interface{}{m}})
  1555. return nil
  1556. }
  1557. func (s *serverRecorder) Start() {}
  1558. func (s *serverRecorder) Stop() {}
  1559. func (s *serverRecorder) AddMember(_ context.Context, m etcdserver.Member) error {
  1560. s.actions = append(s.actions, action{name: "AddMember", params: []interface{}{m}})
  1561. return nil
  1562. }
  1563. func (s *serverRecorder) RemoveMember(_ context.Context, id uint64) error {
  1564. s.actions = append(s.actions, action{name: "RemoveMember", params: []interface{}{id}})
  1565. return nil
  1566. }
  1567. func TestServeAdminMembers(t *testing.T) {
  1568. memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
  1569. memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
  1570. cluster := &fakeCluster{
  1571. members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
  1572. }
  1573. h := &serverHandler{
  1574. server: &serverRecorder{},
  1575. clock: clockwork.NewFakeClock(),
  1576. clusterInfo: cluster,
  1577. }
  1578. msb, err := json.Marshal(
  1579. struct {
  1580. Members []etcdserver.Member `json:"members"`
  1581. }{
  1582. Members: []etcdserver.Member{memb1, memb2},
  1583. },
  1584. )
  1585. if err != nil {
  1586. t.Fatal(err)
  1587. }
  1588. wms := string(msb) + "\n"
  1589. tests := []struct {
  1590. path string
  1591. wcode int
  1592. wct string
  1593. wbody string
  1594. }{
  1595. {adminMembersPrefix, http.StatusOK, "application/json", wms},
  1596. {path.Join(adminMembersPrefix, "100"), http.StatusNotFound, "text/plain; charset=utf-8", "404 page not found\n"},
  1597. {path.Join(adminMembersPrefix, "foobar"), http.StatusNotFound, "text/plain; charset=utf-8", "404 page not found\n"},
  1598. }
  1599. for i, tt := range tests {
  1600. req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
  1601. if err != nil {
  1602. t.Fatal(err)
  1603. }
  1604. rw := httptest.NewRecorder()
  1605. h.serveAdminMembers(rw, req)
  1606. if rw.Code != tt.wcode {
  1607. t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
  1608. }
  1609. if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
  1610. t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
  1611. }
  1612. if rw.Body.String() != tt.wbody {
  1613. t.Errorf("#%d: body = %q, want %q", i, rw.Body.String(), tt.wbody)
  1614. }
  1615. }
  1616. }
  1617. func TestServeAdminMembersPut(t *testing.T) {
  1618. u := mustNewURL(t, adminMembersPrefix)
  1619. raftAttr := etcdserver.RaftAttributes{PeerURLs: []string{"http://127.0.0.1:1"}}
  1620. b, err := json.Marshal(raftAttr)
  1621. if err != nil {
  1622. t.Fatal(err)
  1623. }
  1624. body := bytes.NewReader(b)
  1625. req, err := http.NewRequest("POST", u.String(), body)
  1626. if err != nil {
  1627. t.Fatal(err)
  1628. }
  1629. req.Header.Set("Content-Type", "application/json")
  1630. s := &serverRecorder{}
  1631. h := &serverHandler{
  1632. server: s,
  1633. clock: clockwork.NewFakeClock(),
  1634. }
  1635. rw := httptest.NewRecorder()
  1636. h.serveAdminMembers(rw, req)
  1637. wcode := http.StatusCreated
  1638. if rw.Code != wcode {
  1639. t.Errorf("code=%d, want %d", rw.Code, wcode)
  1640. }
  1641. wm := etcdserver.Member{
  1642. ID: 3064321551348478165,
  1643. RaftAttributes: raftAttr,
  1644. }
  1645. wb, err := json.Marshal(wm)
  1646. if err != nil {
  1647. t.Fatal(err)
  1648. }
  1649. wct := "application/json"
  1650. if gct := rw.Header().Get("Content-Type"); gct != wct {
  1651. t.Errorf("content-type = %s, want %s", gct, wct)
  1652. }
  1653. g := rw.Body.String()
  1654. w := string(wb) + "\n"
  1655. if g != w {
  1656. t.Errorf("got body=%q, want %q", g, w)
  1657. }
  1658. wactions := []action{{name: "AddMember", params: []interface{}{wm}}}
  1659. if !reflect.DeepEqual(s.actions, wactions) {
  1660. t.Errorf("actions = %+v, want %+v", s.actions, wactions)
  1661. }
  1662. }
  1663. func TestServeAdminMembersDelete(t *testing.T) {
  1664. req := &http.Request{
  1665. Method: "DELETE",
  1666. URL: mustNewURL(t, path.Join(adminMembersPrefix, "BEEF")),
  1667. }
  1668. s := &serverRecorder{}
  1669. h := &serverHandler{
  1670. server: s,
  1671. }
  1672. rw := httptest.NewRecorder()
  1673. h.serveAdminMembers(rw, req)
  1674. wcode := http.StatusNoContent
  1675. if rw.Code != wcode {
  1676. t.Errorf("code=%d, want %d", rw.Code, wcode)
  1677. }
  1678. g := rw.Body.String()
  1679. if g != "" {
  1680. t.Errorf("got body=%q, want %q", g, "")
  1681. }
  1682. wactions := []action{{name: "RemoveMember", params: []interface{}{uint64(0xBEEF)}}}
  1683. if !reflect.DeepEqual(s.actions, wactions) {
  1684. t.Errorf("actions = %+v, want %+v", s.actions, wactions)
  1685. }
  1686. }
  1687. func TestTrimEventPrefix(t *testing.T) {
  1688. pre := "/abc"
  1689. tests := []struct {
  1690. ev *store.Event
  1691. wev *store.Event
  1692. }{
  1693. {
  1694. nil,
  1695. nil,
  1696. },
  1697. {
  1698. &store.Event{},
  1699. &store.Event{},
  1700. },
  1701. {
  1702. &store.Event{Node: &store.NodeExtern{Key: "/abc/def"}},
  1703. &store.Event{Node: &store.NodeExtern{Key: "/def"}},
  1704. },
  1705. {
  1706. &store.Event{PrevNode: &store.NodeExtern{Key: "/abc/ghi"}},
  1707. &store.Event{PrevNode: &store.NodeExtern{Key: "/ghi"}},
  1708. },
  1709. {
  1710. &store.Event{
  1711. Node: &store.NodeExtern{Key: "/abc/def"},
  1712. PrevNode: &store.NodeExtern{Key: "/abc/ghi"},
  1713. },
  1714. &store.Event{
  1715. Node: &store.NodeExtern{Key: "/def"},
  1716. PrevNode: &store.NodeExtern{Key: "/ghi"},
  1717. },
  1718. },
  1719. }
  1720. for i, tt := range tests {
  1721. ev := trimEventPrefix(tt.ev, pre)
  1722. if !reflect.DeepEqual(ev, tt.wev) {
  1723. t.Errorf("#%d: event = %+v, want %+v", i, ev, tt.wev)
  1724. }
  1725. }
  1726. }
  1727. func TestTrimNodeExternPrefix(t *testing.T) {
  1728. pre := "/abc"
  1729. tests := []struct {
  1730. n *store.NodeExtern
  1731. wn *store.NodeExtern
  1732. }{
  1733. {
  1734. nil,
  1735. nil,
  1736. },
  1737. {
  1738. &store.NodeExtern{Key: "/abc/def"},
  1739. &store.NodeExtern{Key: "/def"},
  1740. },
  1741. {
  1742. &store.NodeExtern{
  1743. Key: "/abc/def",
  1744. Nodes: []*store.NodeExtern{
  1745. {Key: "/abc/def/1"},
  1746. {Key: "/abc/def/2"},
  1747. },
  1748. },
  1749. &store.NodeExtern{
  1750. Key: "/def",
  1751. Nodes: []*store.NodeExtern{
  1752. {Key: "/def/1"},
  1753. {Key: "/def/2"},
  1754. },
  1755. },
  1756. },
  1757. }
  1758. for i, tt := range tests {
  1759. n := trimNodeExternPrefix(tt.n, pre)
  1760. if !reflect.DeepEqual(n, tt.wn) {
  1761. t.Errorf("#%d: node = %+v, want %+v", i, n, tt.wn)
  1762. }
  1763. }
  1764. }
  1765. type fakeCluster struct {
  1766. id uint64
  1767. clientURLs []string
  1768. members map[uint64]*etcdserver.Member
  1769. }
  1770. func (c *fakeCluster) ID() uint64 { return c.id }
  1771. func (c *fakeCluster) ClientURLs() []string { return c.clientURLs }
  1772. func (c *fakeCluster) Members() []*etcdserver.Member {
  1773. var sms etcdserver.SortableMemberSlice
  1774. for _, m := range c.members {
  1775. sms = append(sms, m)
  1776. }
  1777. sort.Sort(sms)
  1778. return []*etcdserver.Member(sms)
  1779. }
  1780. func (c *fakeCluster) Member(id uint64) *etcdserver.Member { return c.members[id] }