cluster_test.go 14 KB

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