http_test.go 41 KB

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