cluster_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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 etcdserver
  14. import (
  15. "path"
  16. "reflect"
  17. "testing"
  18. "github.com/coreos/etcd/pkg/types"
  19. "github.com/coreos/etcd/store"
  20. )
  21. func TestClusterFromString(t *testing.T) {
  22. tests := []struct {
  23. f string
  24. mems []Member
  25. }{
  26. {
  27. "mem1=http://10.0.0.1:2379,mem1=http://128.193.4.20:2379,mem2=http://10.0.0.2:2379,default=http://127.0.0.1:2379",
  28. []Member{
  29. newTestMember(4322322643958477905, []string{"http://10.0.0.1:2379", "http://128.193.4.20:2379"}, "mem1", nil),
  30. newTestMember(3141198903430435750, []string{"http://10.0.0.2:2379"}, "mem2", nil),
  31. newTestMember(12762790032478827328, []string{"http://127.0.0.1:2379"}, "default", nil),
  32. },
  33. },
  34. }
  35. for i, tt := range tests {
  36. c, err := NewClusterFromString("abc", tt.f)
  37. if err != nil {
  38. t.Fatalf("#%d: unexpected new error: %v", i, err)
  39. }
  40. if c.token != "abc" {
  41. t.Errorf("#%d: token = %v, want abc", i, c.token)
  42. }
  43. wc := newTestCluster(tt.mems)
  44. if !reflect.DeepEqual(c.members, wc.members) {
  45. t.Errorf("#%d: members = %+v, want %+v", i, c.members, wc.members)
  46. }
  47. }
  48. }
  49. func TestClusterFromStringBad(t *testing.T) {
  50. tests := []string{
  51. // invalid URL
  52. "%^",
  53. // no URL defined for member
  54. "mem1=,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
  55. "mem1,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
  56. // bad URL for member
  57. "default=http://localhost/",
  58. // TODO(philips): anyone know of a 64 bit sha1 hash collision
  59. // "06b2f82fd81b2c20=http://128.193.4.20:2379,02c60cb75083ceef=http://128.193.4.20:2379",
  60. // the same url for two members
  61. "mem1=http://128.193.4.20:2379,mem2=http://128.193.4.20:2379",
  62. }
  63. for i, tt := range tests {
  64. if _, err := NewClusterFromString("abc", tt); err == nil {
  65. t.Errorf("#%d: unexpected successful new, want err", i)
  66. }
  67. }
  68. }
  69. func TestClusterFromStore(t *testing.T) {
  70. tests := []struct {
  71. mems []Member
  72. }{
  73. {
  74. []Member{newTestMember(1, nil, "node1", nil)},
  75. },
  76. {
  77. []Member{},
  78. },
  79. {
  80. []Member{
  81. newTestMember(1, nil, "node1", nil),
  82. newTestMember(2, nil, "node2", nil),
  83. },
  84. },
  85. }
  86. for i, tt := range tests {
  87. st := store.New()
  88. hc := newTestCluster(nil)
  89. hc.SetStore(st)
  90. for _, m := range tt.mems {
  91. hc.AddMember(&m)
  92. }
  93. c := NewClusterFromStore("abc", st)
  94. if c.token != "abc" {
  95. t.Errorf("#%d: token = %v, want %v", i, c.token, "abc")
  96. }
  97. wc := newTestCluster(tt.mems)
  98. if !reflect.DeepEqual(c.members, wc.members) {
  99. t.Errorf("#%d: members = %v, want %v", i, c.members, wc.members)
  100. }
  101. }
  102. }
  103. func TestClusterMember(t *testing.T) {
  104. membs := []Member{
  105. newTestMember(1, nil, "node1", nil),
  106. newTestMember(2, nil, "node2", nil),
  107. }
  108. tests := []struct {
  109. id types.ID
  110. match bool
  111. }{
  112. {1, true},
  113. {2, true},
  114. {3, false},
  115. }
  116. for i, tt := range tests {
  117. c := newTestCluster(membs)
  118. m := c.Member(tt.id)
  119. if g := m != nil; g != tt.match {
  120. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  121. }
  122. if m != nil && m.ID != tt.id {
  123. t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
  124. }
  125. }
  126. }
  127. func TestClusterMemberByName(t *testing.T) {
  128. membs := []Member{
  129. newTestMember(1, nil, "node1", nil),
  130. newTestMember(2, nil, "node2", nil),
  131. }
  132. tests := []struct {
  133. name string
  134. match bool
  135. }{
  136. {"node1", true},
  137. {"node2", true},
  138. {"node3", false},
  139. }
  140. for i, tt := range tests {
  141. c := newTestCluster(membs)
  142. m := c.MemberByName(tt.name)
  143. if g := m != nil; g != tt.match {
  144. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  145. }
  146. if m != nil && m.Name != tt.name {
  147. t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
  148. }
  149. }
  150. }
  151. func TestClusterMemberIDs(t *testing.T) {
  152. c := newTestCluster([]Member{
  153. newTestMember(1, nil, "", nil),
  154. newTestMember(4, nil, "", nil),
  155. newTestMember(100, nil, "", nil),
  156. })
  157. w := []types.ID{1, 4, 100}
  158. g := c.MemberIDs()
  159. if !reflect.DeepEqual(w, g) {
  160. t.Errorf("IDs = %+v, want %+v", g, w)
  161. }
  162. }
  163. func TestClusterPeerURLs(t *testing.T) {
  164. tests := []struct {
  165. mems []Member
  166. wurls []string
  167. }{
  168. // single peer with a single address
  169. {
  170. mems: []Member{
  171. newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
  172. },
  173. wurls: []string{"http://192.0.2.1"},
  174. },
  175. // single peer with a single address with a port
  176. {
  177. mems: []Member{
  178. newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
  179. },
  180. wurls: []string{"http://192.0.2.1:8001"},
  181. },
  182. // several members explicitly unsorted
  183. {
  184. mems: []Member{
  185. newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
  186. newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
  187. newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
  188. },
  189. wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  190. },
  191. // no members
  192. {
  193. mems: []Member{},
  194. wurls: []string{},
  195. },
  196. // peer with no peer urls
  197. {
  198. mems: []Member{
  199. newTestMember(3, []string{}, "", nil),
  200. },
  201. wurls: []string{},
  202. },
  203. }
  204. for i, tt := range tests {
  205. c := newTestCluster(tt.mems)
  206. urls := c.PeerURLs()
  207. if !reflect.DeepEqual(urls, tt.wurls) {
  208. t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
  209. }
  210. }
  211. }
  212. func TestClusterClientURLs(t *testing.T) {
  213. tests := []struct {
  214. mems []Member
  215. wurls []string
  216. }{
  217. // single peer with a single address
  218. {
  219. mems: []Member{
  220. newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
  221. },
  222. wurls: []string{"http://192.0.2.1"},
  223. },
  224. // single peer with a single address with a port
  225. {
  226. mems: []Member{
  227. newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
  228. },
  229. wurls: []string{"http://192.0.2.1:8001"},
  230. },
  231. // several members explicitly unsorted
  232. {
  233. mems: []Member{
  234. newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
  235. newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
  236. newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
  237. },
  238. wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  239. },
  240. // no members
  241. {
  242. mems: []Member{},
  243. wurls: []string{},
  244. },
  245. // peer with no client urls
  246. {
  247. mems: []Member{
  248. newTestMember(3, nil, "", []string{}),
  249. },
  250. wurls: []string{},
  251. },
  252. }
  253. for i, tt := range tests {
  254. c := newTestCluster(tt.mems)
  255. urls := c.ClientURLs()
  256. if !reflect.DeepEqual(urls, tt.wurls) {
  257. t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
  258. }
  259. }
  260. }
  261. func TestClusterValidateAndAssignIDsBad(t *testing.T) {
  262. tests := []struct {
  263. clmembs []Member
  264. membs []*Member
  265. }{
  266. {
  267. // unmatched length
  268. []Member{
  269. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  270. },
  271. []*Member{},
  272. },
  273. {
  274. // unmatched peer urls
  275. []Member{
  276. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  277. },
  278. []*Member{
  279. newTestMemberp(1, []string{"http://127.0.0.1:4001"}, "", nil),
  280. },
  281. },
  282. {
  283. // unmatched peer urls
  284. []Member{
  285. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  286. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  287. },
  288. []*Member{
  289. newTestMemberp(1, []string{"http://127.0.0.1:2379"}, "", nil),
  290. newTestMemberp(2, []string{"http://127.0.0.2:4001"}, "", nil),
  291. },
  292. },
  293. }
  294. for i, tt := range tests {
  295. cl := newTestCluster(tt.clmembs)
  296. if err := cl.ValidateAndAssignIDs(tt.membs); err == nil {
  297. t.Errorf("#%d: unexpected update success", i)
  298. }
  299. }
  300. }
  301. func TestClusterValidateAndAssignIDs(t *testing.T) {
  302. tests := []struct {
  303. clmembs []Member
  304. membs []*Member
  305. wids []types.ID
  306. }{
  307. {
  308. []Member{
  309. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  310. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  311. },
  312. []*Member{
  313. newTestMemberp(3, []string{"http://127.0.0.1:2379"}, "", nil),
  314. newTestMemberp(4, []string{"http://127.0.0.2:2379"}, "", nil),
  315. },
  316. []types.ID{3, 4},
  317. },
  318. }
  319. for i, tt := range tests {
  320. cl := newTestCluster(tt.clmembs)
  321. if err := cl.ValidateAndAssignIDs(tt.membs); err != nil {
  322. t.Errorf("#%d: unexpect update error: %v", i, err)
  323. }
  324. if !reflect.DeepEqual(cl.MemberIDs(), tt.wids) {
  325. t.Errorf("#%d: ids = %v, want %v", i, cl.MemberIDs(), tt.wids)
  326. }
  327. }
  328. }
  329. func TestClusterGenID(t *testing.T) {
  330. cs := newTestCluster([]Member{
  331. newTestMember(1, nil, "", nil),
  332. newTestMember(2, nil, "", nil),
  333. })
  334. cs.genID()
  335. if cs.ID() == 0 {
  336. t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
  337. }
  338. previd := cs.ID()
  339. cs.SetStore(&storeRecorder{})
  340. cs.AddMember(newTestMemberp(3, nil, "", nil))
  341. cs.genID()
  342. if cs.ID() == previd {
  343. t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
  344. }
  345. }
  346. func TestNodeToMemberBad(t *testing.T) {
  347. tests := []*store.NodeExtern{
  348. {Key: "/1234", Nodes: []*store.NodeExtern{
  349. {Key: "/1234/strange"},
  350. }},
  351. {Key: "/1234", Nodes: []*store.NodeExtern{
  352. {Key: "/1234/dynamic", Value: stringp("garbage")},
  353. }},
  354. {Key: "/1234", Nodes: []*store.NodeExtern{
  355. {Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)},
  356. }},
  357. {Key: "/1234", Nodes: []*store.NodeExtern{
  358. {Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)},
  359. {Key: "/1234/strange"},
  360. }},
  361. {Key: "/1234", Nodes: []*store.NodeExtern{
  362. {Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)},
  363. {Key: "/1234/static", Value: stringp("garbage")},
  364. }},
  365. {Key: "/1234", Nodes: []*store.NodeExtern{
  366. {Key: "/1234/dynamic", Value: stringp(`{"peerURLs":null}`)},
  367. {Key: "/1234/static", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  368. {Key: "/1234/strange"},
  369. }},
  370. }
  371. for i, tt := range tests {
  372. if _, err := nodeToMember(tt); err == nil {
  373. t.Errorf("#%d: unexpected nil error", i)
  374. }
  375. }
  376. }
  377. func TestClusterAddMember(t *testing.T) {
  378. st := &storeRecorder{}
  379. c := newTestCluster(nil)
  380. c.SetStore(st)
  381. c.AddMember(newTestMemberp(1, nil, "node1", nil))
  382. wactions := []action{
  383. {
  384. name: "Create",
  385. params: []interface{}{
  386. path.Join(storeMembersPrefix, "1", "raftAttributes"),
  387. false,
  388. `{"peerURLs":null}`,
  389. false,
  390. store.Permanent,
  391. },
  392. },
  393. {
  394. name: "Create",
  395. params: []interface{}{
  396. path.Join(storeMembersPrefix, "1", "attributes"),
  397. false,
  398. `{"name":"node1"}`,
  399. false,
  400. store.Permanent,
  401. },
  402. },
  403. }
  404. if g := st.Action(); !reflect.DeepEqual(g, wactions) {
  405. t.Errorf("actions = %v, want %v", g, wactions)
  406. }
  407. }
  408. func TestClusterMembers(t *testing.T) {
  409. cls := &Cluster{
  410. members: map[types.ID]*Member{
  411. 1: &Member{ID: 1},
  412. 20: &Member{ID: 20},
  413. 100: &Member{ID: 100},
  414. 5: &Member{ID: 5},
  415. 50: &Member{ID: 50},
  416. },
  417. }
  418. w := []*Member{
  419. &Member{ID: 1},
  420. &Member{ID: 5},
  421. &Member{ID: 20},
  422. &Member{ID: 50},
  423. &Member{ID: 100},
  424. }
  425. if g := cls.Members(); !reflect.DeepEqual(g, w) {
  426. t.Fatalf("Members()=%#v, want %#v", g, w)
  427. }
  428. }
  429. func TestClusterString(t *testing.T) {
  430. cls := &Cluster{
  431. members: map[types.ID]*Member{
  432. 1: newTestMemberp(
  433. 1,
  434. []string{"http://1.1.1.1:1111", "http://0.0.0.0:0000"},
  435. "abc",
  436. nil,
  437. ),
  438. 2: newTestMemberp(
  439. 2,
  440. []string{"http://2.2.2.2:2222"},
  441. "def",
  442. nil,
  443. ),
  444. 3: newTestMemberp(
  445. 3,
  446. []string{"http://3.3.3.3:1234", "http://127.0.0.1:7001"},
  447. "ghi",
  448. nil,
  449. ),
  450. // no PeerURLs = not included
  451. 4: newTestMemberp(
  452. 4,
  453. []string{},
  454. "four",
  455. nil,
  456. ),
  457. 5: newTestMemberp(
  458. 5,
  459. nil,
  460. "five",
  461. nil,
  462. ),
  463. },
  464. }
  465. w := "abc=http://0.0.0.0:0000,abc=http://1.1.1.1:1111,def=http://2.2.2.2:2222,ghi=http://127.0.0.1:7001,ghi=http://3.3.3.3:1234"
  466. if g := cls.String(); g != w {
  467. t.Fatalf("Cluster.String():\ngot %#v\nwant %#v", g, w)
  468. }
  469. }
  470. func TestClusterRemoveMember(t *testing.T) {
  471. st := &storeRecorder{}
  472. c := newTestCluster(nil)
  473. c.SetStore(st)
  474. c.RemoveMember(1)
  475. wactions := []action{
  476. {name: "Delete", params: []interface{}{memberStoreKey(1), true, true}},
  477. {name: "Create", params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
  478. }
  479. if !reflect.DeepEqual(st.Action(), wactions) {
  480. t.Errorf("actions = %v, want %v", st.Action(), wactions)
  481. }
  482. }
  483. func TestNodeToMember(t *testing.T) {
  484. n := &store.NodeExtern{Key: "/1234", Nodes: []*store.NodeExtern{
  485. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  486. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  487. }}
  488. wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
  489. m, err := nodeToMember(n)
  490. if err != nil {
  491. t.Fatalf("unexpected nodeToMember error: %v", err)
  492. }
  493. if !reflect.DeepEqual(m, wm) {
  494. t.Errorf("member = %+v, want %+v", m, wm)
  495. }
  496. }
  497. func newTestCluster(membs []Member) *Cluster {
  498. c := &Cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
  499. for i, m := range membs {
  500. c.members[m.ID] = &membs[i]
  501. }
  502. return c
  503. }
  504. func newTestMember(id uint64, peerURLs []string, name string, clientURLs []string) Member {
  505. return Member{
  506. ID: types.ID(id),
  507. RaftAttributes: RaftAttributes{PeerURLs: peerURLs},
  508. Attributes: Attributes{Name: name, ClientURLs: clientURLs},
  509. }
  510. }
  511. func newTestMemberp(id uint64, peerURLs []string, name string, clientURLs []string) *Member {
  512. m := newTestMember(id, peerURLs, name, clientURLs)
  513. return &m
  514. }