http_test.go 40 KB

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