cluster_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package etcdserver
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "path"
  19. "reflect"
  20. "testing"
  21. "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
  22. "github.com/coreos/etcd/pkg/testutil"
  23. "github.com/coreos/etcd/pkg/types"
  24. "github.com/coreos/etcd/raft/raftpb"
  25. "github.com/coreos/etcd/store"
  26. )
  27. func TestClusterFromString(t *testing.T) {
  28. tests := []struct {
  29. f string
  30. mems []*Member
  31. }{
  32. {
  33. "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",
  34. []*Member{
  35. newTestMember(3141198903430435750, []string{"http://10.0.0.2:2379"}, "mem2", nil),
  36. newTestMember(4322322643958477905, []string{"http://10.0.0.1:2379", "http://128.193.4.20:2379"}, "mem1", nil),
  37. newTestMember(12762790032478827328, []string{"http://127.0.0.1:2379"}, "default", nil),
  38. },
  39. },
  40. }
  41. for i, tt := range tests {
  42. c, err := NewClusterFromString("abc", tt.f)
  43. if err != nil {
  44. t.Fatalf("#%d: unexpected new error: %v", i, err)
  45. }
  46. if c.token != "abc" {
  47. t.Errorf("#%d: token = %v, want abc", i, c.token)
  48. }
  49. if !reflect.DeepEqual(c.Members(), tt.mems) {
  50. t.Errorf("#%d: members = %+v, want %+v", i, c.Members(), tt.mems)
  51. }
  52. }
  53. }
  54. func TestClusterFromStringBad(t *testing.T) {
  55. tests := []string{
  56. // invalid URL
  57. "%^",
  58. // no URL defined for member
  59. "mem1=,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
  60. "mem1,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
  61. // bad URL for member
  62. "default=http://localhost/",
  63. // TODO(philips): anyone know of a 64 bit sha1 hash collision
  64. // "06b2f82fd81b2c20=http://128.193.4.20:2379,02c60cb75083ceef=http://128.193.4.20:2379",
  65. // the same url for two members
  66. "mem1=http://128.193.4.20:2379,mem2=http://128.193.4.20:2379",
  67. }
  68. for i, tt := range tests {
  69. if _, err := NewClusterFromString("abc", tt); err == nil {
  70. t.Errorf("#%d: unexpected successful new, want err", i)
  71. }
  72. }
  73. }
  74. func TestClusterFromStore(t *testing.T) {
  75. tests := []struct {
  76. mems []*Member
  77. ver *semver.Version
  78. }{
  79. {
  80. []*Member{newTestMember(1, nil, "", nil)},
  81. semver.Must(semver.NewVersion("2.0.0")),
  82. },
  83. {
  84. nil,
  85. nil,
  86. },
  87. {
  88. []*Member{
  89. newTestMember(1, nil, "", nil),
  90. newTestMember(2, nil, "", nil),
  91. },
  92. semver.Must(semver.NewVersion("2.0.0")),
  93. },
  94. }
  95. for i, tt := range tests {
  96. st := store.New()
  97. hc := newTestCluster(nil)
  98. hc.SetStore(st)
  99. for _, m := range tt.mems {
  100. hc.AddMember(m)
  101. }
  102. if tt.ver != nil {
  103. _, err := st.Set(path.Join(StoreClusterPrefix, "version"), false, tt.ver.String(), store.Permanent)
  104. if err != nil {
  105. t.Fatal(err)
  106. }
  107. }
  108. c := NewClusterFromStore("abc", st)
  109. if c.token != "abc" {
  110. t.Errorf("#%d: token = %v, want %v", i, c.token, "abc")
  111. }
  112. if !reflect.DeepEqual(c.Members(), tt.mems) {
  113. t.Errorf("#%d: members = %v, want %v", i, c.Members(), tt.mems)
  114. }
  115. if !reflect.DeepEqual(c.Version(), tt.ver) {
  116. t.Errorf("#%d: ver = %v, want %v", i, c.Version(), tt.ver)
  117. }
  118. }
  119. }
  120. func TestClusterMember(t *testing.T) {
  121. membs := []*Member{
  122. newTestMember(1, nil, "node1", nil),
  123. newTestMember(2, nil, "node2", nil),
  124. }
  125. tests := []struct {
  126. id types.ID
  127. match bool
  128. }{
  129. {1, true},
  130. {2, true},
  131. {3, false},
  132. }
  133. for i, tt := range tests {
  134. c := newTestCluster(membs)
  135. m := c.Member(tt.id)
  136. if g := m != nil; g != tt.match {
  137. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  138. }
  139. if m != nil && m.ID != tt.id {
  140. t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
  141. }
  142. }
  143. }
  144. func TestClusterMemberByName(t *testing.T) {
  145. membs := []*Member{
  146. newTestMember(1, nil, "node1", nil),
  147. newTestMember(2, nil, "node2", nil),
  148. }
  149. tests := []struct {
  150. name string
  151. match bool
  152. }{
  153. {"node1", true},
  154. {"node2", true},
  155. {"node3", false},
  156. }
  157. for i, tt := range tests {
  158. c := newTestCluster(membs)
  159. m := c.MemberByName(tt.name)
  160. if g := m != nil; g != tt.match {
  161. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  162. }
  163. if m != nil && m.Name != tt.name {
  164. t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
  165. }
  166. }
  167. }
  168. func TestClusterMemberIDs(t *testing.T) {
  169. c := newTestCluster([]*Member{
  170. newTestMember(1, nil, "", nil),
  171. newTestMember(4, nil, "", nil),
  172. newTestMember(100, nil, "", nil),
  173. })
  174. w := []types.ID{1, 4, 100}
  175. g := c.MemberIDs()
  176. if !reflect.DeepEqual(w, g) {
  177. t.Errorf("IDs = %+v, want %+v", g, w)
  178. }
  179. }
  180. func TestClusterPeerURLs(t *testing.T) {
  181. tests := []struct {
  182. mems []*Member
  183. wurls []string
  184. }{
  185. // single peer with a single address
  186. {
  187. mems: []*Member{
  188. newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
  189. },
  190. wurls: []string{"http://192.0.2.1"},
  191. },
  192. // single peer with a single address with a port
  193. {
  194. mems: []*Member{
  195. newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
  196. },
  197. wurls: []string{"http://192.0.2.1:8001"},
  198. },
  199. // several members explicitly unsorted
  200. {
  201. mems: []*Member{
  202. newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
  203. newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
  204. newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
  205. },
  206. 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"},
  207. },
  208. // no members
  209. {
  210. mems: []*Member{},
  211. wurls: []string{},
  212. },
  213. // peer with no peer urls
  214. {
  215. mems: []*Member{
  216. newTestMember(3, []string{}, "", nil),
  217. },
  218. wurls: []string{},
  219. },
  220. }
  221. for i, tt := range tests {
  222. c := newTestCluster(tt.mems)
  223. urls := c.PeerURLs()
  224. if !reflect.DeepEqual(urls, tt.wurls) {
  225. t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
  226. }
  227. }
  228. }
  229. func TestClusterClientURLs(t *testing.T) {
  230. tests := []struct {
  231. mems []*Member
  232. wurls []string
  233. }{
  234. // single peer with a single address
  235. {
  236. mems: []*Member{
  237. newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
  238. },
  239. wurls: []string{"http://192.0.2.1"},
  240. },
  241. // single peer with a single address with a port
  242. {
  243. mems: []*Member{
  244. newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
  245. },
  246. wurls: []string{"http://192.0.2.1:8001"},
  247. },
  248. // several members explicitly unsorted
  249. {
  250. mems: []*Member{
  251. newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
  252. newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
  253. newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
  254. },
  255. 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"},
  256. },
  257. // no members
  258. {
  259. mems: []*Member{},
  260. wurls: []string{},
  261. },
  262. // peer with no client urls
  263. {
  264. mems: []*Member{
  265. newTestMember(3, nil, "", []string{}),
  266. },
  267. wurls: []string{},
  268. },
  269. }
  270. for i, tt := range tests {
  271. c := newTestCluster(tt.mems)
  272. urls := c.ClientURLs()
  273. if !reflect.DeepEqual(urls, tt.wurls) {
  274. t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
  275. }
  276. }
  277. }
  278. func TestClusterValidateAndAssignIDsBad(t *testing.T) {
  279. tests := []struct {
  280. clmembs []*Member
  281. membs []*Member
  282. }{
  283. {
  284. // unmatched length
  285. []*Member{
  286. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  287. },
  288. []*Member{},
  289. },
  290. {
  291. // unmatched peer urls
  292. []*Member{
  293. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  294. },
  295. []*Member{
  296. newTestMember(1, []string{"http://127.0.0.1:4001"}, "", nil),
  297. },
  298. },
  299. {
  300. // unmatched peer urls
  301. []*Member{
  302. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  303. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  304. },
  305. []*Member{
  306. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  307. newTestMember(2, []string{"http://127.0.0.2:4001"}, "", nil),
  308. },
  309. },
  310. }
  311. for i, tt := range tests {
  312. ecl := newTestCluster(tt.clmembs)
  313. lcl := newTestCluster(tt.membs)
  314. if err := ValidateClusterAndAssignIDs(lcl, ecl); err == nil {
  315. t.Errorf("#%d: unexpected update success", i)
  316. }
  317. }
  318. }
  319. func TestClusterValidateAndAssignIDs(t *testing.T) {
  320. tests := []struct {
  321. clmembs []*Member
  322. membs []*Member
  323. wids []types.ID
  324. }{
  325. {
  326. []*Member{
  327. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  328. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  329. },
  330. []*Member{
  331. newTestMember(3, []string{"http://127.0.0.1:2379"}, "", nil),
  332. newTestMember(4, []string{"http://127.0.0.2:2379"}, "", nil),
  333. },
  334. []types.ID{3, 4},
  335. },
  336. }
  337. for i, tt := range tests {
  338. lcl := newTestCluster(tt.clmembs)
  339. ecl := newTestCluster(tt.membs)
  340. if err := ValidateClusterAndAssignIDs(lcl, ecl); err != nil {
  341. t.Errorf("#%d: unexpect update error: %v", i, err)
  342. }
  343. if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
  344. t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
  345. }
  346. }
  347. }
  348. func TestClusterValidateConfigurationChange(t *testing.T) {
  349. cl := newCluster("")
  350. cl.SetStore(store.New())
  351. for i := 1; i <= 4; i++ {
  352. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", i)}}
  353. cl.AddMember(&Member{ID: types.ID(i), RaftAttributes: attr})
  354. }
  355. cl.RemoveMember(4)
  356. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
  357. ctx, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  358. if err != nil {
  359. t.Fatal(err)
  360. }
  361. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  362. ctx5, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  363. if err != nil {
  364. t.Fatal(err)
  365. }
  366. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 3)}}
  367. ctx2to3, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  368. if err != nil {
  369. t.Fatal(err)
  370. }
  371. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  372. ctx2to5, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. tests := []struct {
  377. cc raftpb.ConfChange
  378. werr error
  379. }{
  380. {
  381. raftpb.ConfChange{
  382. Type: raftpb.ConfChangeRemoveNode,
  383. NodeID: 3,
  384. },
  385. nil,
  386. },
  387. {
  388. raftpb.ConfChange{
  389. Type: raftpb.ConfChangeAddNode,
  390. NodeID: 4,
  391. },
  392. ErrIDRemoved,
  393. },
  394. {
  395. raftpb.ConfChange{
  396. Type: raftpb.ConfChangeRemoveNode,
  397. NodeID: 4,
  398. },
  399. ErrIDRemoved,
  400. },
  401. {
  402. raftpb.ConfChange{
  403. Type: raftpb.ConfChangeAddNode,
  404. NodeID: 1,
  405. },
  406. ErrIDExists,
  407. },
  408. {
  409. raftpb.ConfChange{
  410. Type: raftpb.ConfChangeAddNode,
  411. NodeID: 5,
  412. Context: ctx,
  413. },
  414. ErrPeerURLexists,
  415. },
  416. {
  417. raftpb.ConfChange{
  418. Type: raftpb.ConfChangeRemoveNode,
  419. NodeID: 5,
  420. },
  421. ErrIDNotFound,
  422. },
  423. {
  424. raftpb.ConfChange{
  425. Type: raftpb.ConfChangeAddNode,
  426. NodeID: 5,
  427. Context: ctx5,
  428. },
  429. nil,
  430. },
  431. {
  432. raftpb.ConfChange{
  433. Type: raftpb.ConfChangeUpdateNode,
  434. NodeID: 5,
  435. Context: ctx,
  436. },
  437. ErrIDNotFound,
  438. },
  439. // try to change the peer url of 2 to the peer url of 3
  440. {
  441. raftpb.ConfChange{
  442. Type: raftpb.ConfChangeUpdateNode,
  443. NodeID: 2,
  444. Context: ctx2to3,
  445. },
  446. ErrPeerURLexists,
  447. },
  448. {
  449. raftpb.ConfChange{
  450. Type: raftpb.ConfChangeUpdateNode,
  451. NodeID: 2,
  452. Context: ctx2to5,
  453. },
  454. nil,
  455. },
  456. }
  457. for i, tt := range tests {
  458. err := cl.ValidateConfigurationChange(tt.cc)
  459. if err != tt.werr {
  460. t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
  461. }
  462. }
  463. }
  464. func TestClusterGenID(t *testing.T) {
  465. cs := newTestCluster([]*Member{
  466. newTestMember(1, nil, "", nil),
  467. newTestMember(2, nil, "", nil),
  468. })
  469. cs.genID()
  470. if cs.ID() == 0 {
  471. t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
  472. }
  473. previd := cs.ID()
  474. cs.SetStore(&storeRecorder{})
  475. cs.AddMember(newTestMember(3, nil, "", nil))
  476. cs.genID()
  477. if cs.ID() == previd {
  478. t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
  479. }
  480. }
  481. func TestNodeToMemberBad(t *testing.T) {
  482. tests := []*store.NodeExtern{
  483. {Key: "/1234", Nodes: []*store.NodeExtern{
  484. {Key: "/1234/strange"},
  485. }},
  486. {Key: "/1234", Nodes: []*store.NodeExtern{
  487. {Key: "/1234/raftAttributes", Value: stringp("garbage")},
  488. }},
  489. {Key: "/1234", Nodes: []*store.NodeExtern{
  490. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  491. }},
  492. {Key: "/1234", Nodes: []*store.NodeExtern{
  493. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  494. {Key: "/1234/strange"},
  495. }},
  496. {Key: "/1234", Nodes: []*store.NodeExtern{
  497. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  498. {Key: "/1234/attributes", Value: stringp("garbage")},
  499. }},
  500. {Key: "/1234", Nodes: []*store.NodeExtern{
  501. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  502. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  503. {Key: "/1234/strange"},
  504. }},
  505. }
  506. for i, tt := range tests {
  507. if _, err := nodeToMember(tt); err == nil {
  508. t.Errorf("#%d: unexpected nil error", i)
  509. }
  510. }
  511. }
  512. func TestClusterAddMember(t *testing.T) {
  513. st := &storeRecorder{}
  514. c := newTestCluster(nil)
  515. c.SetStore(st)
  516. c.AddMember(newTestMember(1, nil, "node1", nil))
  517. wactions := []testutil.Action{
  518. {
  519. Name: "Create",
  520. Params: []interface{}{
  521. path.Join(storeMembersPrefix, "1", "raftAttributes"),
  522. false,
  523. `{"peerURLs":null}`,
  524. false,
  525. store.Permanent,
  526. },
  527. },
  528. }
  529. if g := st.Action(); !reflect.DeepEqual(g, wactions) {
  530. t.Errorf("actions = %v, want %v", g, wactions)
  531. }
  532. }
  533. func TestClusterMembers(t *testing.T) {
  534. cls := &Cluster{
  535. members: map[types.ID]*Member{
  536. 1: &Member{ID: 1},
  537. 20: &Member{ID: 20},
  538. 100: &Member{ID: 100},
  539. 5: &Member{ID: 5},
  540. 50: &Member{ID: 50},
  541. },
  542. }
  543. w := []*Member{
  544. &Member{ID: 1},
  545. &Member{ID: 5},
  546. &Member{ID: 20},
  547. &Member{ID: 50},
  548. &Member{ID: 100},
  549. }
  550. if g := cls.Members(); !reflect.DeepEqual(g, w) {
  551. t.Fatalf("Members()=%#v, want %#v", g, w)
  552. }
  553. }
  554. func TestClusterString(t *testing.T) {
  555. cls := &Cluster{
  556. members: map[types.ID]*Member{
  557. 1: newTestMember(
  558. 1,
  559. []string{"http://1.1.1.1:1111", "http://0.0.0.0:0000"},
  560. "abc",
  561. nil,
  562. ),
  563. 2: newTestMember(
  564. 2,
  565. []string{"http://2.2.2.2:2222"},
  566. "def",
  567. nil,
  568. ),
  569. 3: newTestMember(
  570. 3,
  571. []string{"http://3.3.3.3:1234", "http://127.0.0.1:2380"},
  572. "ghi",
  573. nil,
  574. ),
  575. // no PeerURLs = not included
  576. 4: newTestMember(
  577. 4,
  578. []string{},
  579. "four",
  580. nil,
  581. ),
  582. 5: newTestMember(
  583. 5,
  584. nil,
  585. "five",
  586. nil,
  587. ),
  588. },
  589. }
  590. 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:2380,ghi=http://3.3.3.3:1234"
  591. if g := cls.String(); g != w {
  592. t.Fatalf("Cluster.String():\ngot %#v\nwant %#v", g, w)
  593. }
  594. }
  595. func TestClusterRemoveMember(t *testing.T) {
  596. st := &storeRecorder{}
  597. c := newTestCluster(nil)
  598. c.SetStore(st)
  599. c.RemoveMember(1)
  600. wactions := []testutil.Action{
  601. {Name: "Delete", Params: []interface{}{memberStoreKey(1), true, true}},
  602. {Name: "Create", Params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
  603. }
  604. if !reflect.DeepEqual(st.Action(), wactions) {
  605. t.Errorf("actions = %v, want %v", st.Action(), wactions)
  606. }
  607. }
  608. func TestNodeToMember(t *testing.T) {
  609. n := &store.NodeExtern{Key: "/1234", Nodes: []*store.NodeExtern{
  610. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  611. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  612. }}
  613. wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
  614. m, err := nodeToMember(n)
  615. if err != nil {
  616. t.Fatalf("unexpected nodeToMember error: %v", err)
  617. }
  618. if !reflect.DeepEqual(m, wm) {
  619. t.Errorf("member = %+v, want %+v", m, wm)
  620. }
  621. }
  622. func newTestCluster(membs []*Member) *Cluster {
  623. c := &Cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
  624. for _, m := range membs {
  625. c.members[m.ID] = m
  626. }
  627. return c
  628. }
  629. func stringp(s string) *string { return &s }