http_test.go 36 KB

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