cluster_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. // Copyright 2015 The etcd Authors
  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 membership
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "path"
  19. "reflect"
  20. "testing"
  21. "go.etcd.io/etcd/etcdserver/api/v2store"
  22. "go.etcd.io/etcd/pkg/mock/mockstore"
  23. "go.etcd.io/etcd/pkg/testutil"
  24. "go.etcd.io/etcd/pkg/types"
  25. "go.etcd.io/etcd/raft/raftpb"
  26. "go.uber.org/zap"
  27. )
  28. func TestClusterMember(t *testing.T) {
  29. membs := []*Member{
  30. newTestMember(1, nil, "node1", nil),
  31. newTestMember(2, nil, "node2", nil),
  32. }
  33. tests := []struct {
  34. id types.ID
  35. match bool
  36. }{
  37. {1, true},
  38. {2, true},
  39. {3, false},
  40. }
  41. for i, tt := range tests {
  42. c := newTestCluster(membs)
  43. m := c.Member(tt.id)
  44. if g := m != nil; g != tt.match {
  45. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  46. }
  47. if m != nil && m.ID != tt.id {
  48. t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
  49. }
  50. }
  51. }
  52. func TestClusterMemberByName(t *testing.T) {
  53. membs := []*Member{
  54. newTestMember(1, nil, "node1", nil),
  55. newTestMember(2, nil, "node2", nil),
  56. }
  57. tests := []struct {
  58. name string
  59. match bool
  60. }{
  61. {"node1", true},
  62. {"node2", true},
  63. {"node3", false},
  64. }
  65. for i, tt := range tests {
  66. c := newTestCluster(membs)
  67. m := c.MemberByName(tt.name)
  68. if g := m != nil; g != tt.match {
  69. t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
  70. }
  71. if m != nil && m.Name != tt.name {
  72. t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
  73. }
  74. }
  75. }
  76. func TestClusterMemberIDs(t *testing.T) {
  77. c := newTestCluster([]*Member{
  78. newTestMember(1, nil, "", nil),
  79. newTestMember(4, nil, "", nil),
  80. newTestMember(100, nil, "", nil),
  81. })
  82. w := []types.ID{1, 4, 100}
  83. g := c.MemberIDs()
  84. if !reflect.DeepEqual(w, g) {
  85. t.Errorf("IDs = %+v, want %+v", g, w)
  86. }
  87. }
  88. func TestClusterPeerURLs(t *testing.T) {
  89. tests := []struct {
  90. mems []*Member
  91. wurls []string
  92. }{
  93. // single peer with a single address
  94. {
  95. mems: []*Member{
  96. newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
  97. },
  98. wurls: []string{"http://192.0.2.1"},
  99. },
  100. // single peer with a single address with a port
  101. {
  102. mems: []*Member{
  103. newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
  104. },
  105. wurls: []string{"http://192.0.2.1:8001"},
  106. },
  107. // several members explicitly unsorted
  108. {
  109. mems: []*Member{
  110. newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
  111. newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
  112. newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
  113. },
  114. 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"},
  115. },
  116. // no members
  117. {
  118. mems: []*Member{},
  119. wurls: []string{},
  120. },
  121. // peer with no peer urls
  122. {
  123. mems: []*Member{
  124. newTestMember(3, []string{}, "", nil),
  125. },
  126. wurls: []string{},
  127. },
  128. }
  129. for i, tt := range tests {
  130. c := newTestCluster(tt.mems)
  131. urls := c.PeerURLs()
  132. if !reflect.DeepEqual(urls, tt.wurls) {
  133. t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
  134. }
  135. }
  136. }
  137. func TestClusterClientURLs(t *testing.T) {
  138. tests := []struct {
  139. mems []*Member
  140. wurls []string
  141. }{
  142. // single peer with a single address
  143. {
  144. mems: []*Member{
  145. newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
  146. },
  147. wurls: []string{"http://192.0.2.1"},
  148. },
  149. // single peer with a single address with a port
  150. {
  151. mems: []*Member{
  152. newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
  153. },
  154. wurls: []string{"http://192.0.2.1:8001"},
  155. },
  156. // several members explicitly unsorted
  157. {
  158. mems: []*Member{
  159. newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
  160. newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
  161. newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
  162. },
  163. 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"},
  164. },
  165. // no members
  166. {
  167. mems: []*Member{},
  168. wurls: []string{},
  169. },
  170. // peer with no client urls
  171. {
  172. mems: []*Member{
  173. newTestMember(3, nil, "", []string{}),
  174. },
  175. wurls: []string{},
  176. },
  177. }
  178. for i, tt := range tests {
  179. c := newTestCluster(tt.mems)
  180. urls := c.ClientURLs()
  181. if !reflect.DeepEqual(urls, tt.wurls) {
  182. t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
  183. }
  184. }
  185. }
  186. func TestClusterValidateAndAssignIDsBad(t *testing.T) {
  187. tests := []struct {
  188. clmembs []*Member
  189. membs []*Member
  190. }{
  191. {
  192. // unmatched length
  193. []*Member{
  194. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  195. },
  196. []*Member{},
  197. },
  198. {
  199. // unmatched peer urls
  200. []*Member{
  201. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  202. },
  203. []*Member{
  204. newTestMember(1, []string{"http://127.0.0.1:4001"}, "", nil),
  205. },
  206. },
  207. {
  208. // unmatched peer urls
  209. []*Member{
  210. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  211. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  212. },
  213. []*Member{
  214. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  215. newTestMember(2, []string{"http://127.0.0.2:4001"}, "", nil),
  216. },
  217. },
  218. }
  219. for i, tt := range tests {
  220. ecl := newTestCluster(tt.clmembs)
  221. lcl := newTestCluster(tt.membs)
  222. if err := ValidateClusterAndAssignIDs(zap.NewExample(), lcl, ecl); err == nil {
  223. t.Errorf("#%d: unexpected update success", i)
  224. }
  225. }
  226. }
  227. func TestClusterValidateAndAssignIDs(t *testing.T) {
  228. tests := []struct {
  229. clmembs []*Member
  230. membs []*Member
  231. wids []types.ID
  232. }{
  233. {
  234. []*Member{
  235. newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
  236. newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
  237. },
  238. []*Member{
  239. newTestMember(3, []string{"http://127.0.0.1:2379"}, "", nil),
  240. newTestMember(4, []string{"http://127.0.0.2:2379"}, "", nil),
  241. },
  242. []types.ID{3, 4},
  243. },
  244. }
  245. for i, tt := range tests {
  246. lcl := newTestCluster(tt.clmembs)
  247. ecl := newTestCluster(tt.membs)
  248. if err := ValidateClusterAndAssignIDs(zap.NewExample(), lcl, ecl); err != nil {
  249. t.Errorf("#%d: unexpect update error: %v", i, err)
  250. }
  251. if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
  252. t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
  253. }
  254. }
  255. }
  256. func TestClusterValidateConfigurationChange(t *testing.T) {
  257. cl := NewCluster(zap.NewExample(), "")
  258. cl.SetStore(v2store.New())
  259. for i := 1; i <= 4; i++ {
  260. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", i)}}
  261. cl.AddMember(&Member{ID: types.ID(i), RaftAttributes: attr})
  262. }
  263. cl.RemoveMember(4)
  264. attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
  265. ctx, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  266. if err != nil {
  267. t.Fatal(err)
  268. }
  269. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
  270. ctx1, err := json.Marshal(&Member{ID: types.ID(1), RaftAttributes: attr})
  271. if err != nil {
  272. t.Fatal(err)
  273. }
  274. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  275. ctx5, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
  276. if err != nil {
  277. t.Fatal(err)
  278. }
  279. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 3)}}
  280. ctx2to3, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  281. if err != nil {
  282. t.Fatal(err)
  283. }
  284. attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
  285. ctx2to5, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. ctx3, err := json.Marshal(&ConfigChangeContext{Member: Member{ID: types.ID(3), RaftAttributes: attr}, IsPromote: true})
  290. if err != nil {
  291. t.Fatal(err)
  292. }
  293. ctx6, err := json.Marshal(&ConfigChangeContext{Member: Member{ID: types.ID(6), RaftAttributes: attr}, IsPromote: true})
  294. if err != nil {
  295. t.Fatal(err)
  296. }
  297. tests := []struct {
  298. cc raftpb.ConfChange
  299. werr error
  300. }{
  301. {
  302. raftpb.ConfChange{
  303. Type: raftpb.ConfChangeRemoveNode,
  304. NodeID: 3,
  305. },
  306. nil,
  307. },
  308. {
  309. raftpb.ConfChange{
  310. Type: raftpb.ConfChangeAddNode,
  311. NodeID: 4,
  312. },
  313. ErrIDRemoved,
  314. },
  315. {
  316. raftpb.ConfChange{
  317. Type: raftpb.ConfChangeRemoveNode,
  318. NodeID: 4,
  319. },
  320. ErrIDRemoved,
  321. },
  322. {
  323. raftpb.ConfChange{
  324. Type: raftpb.ConfChangeAddNode,
  325. NodeID: 1,
  326. Context: ctx1,
  327. },
  328. ErrIDExists,
  329. },
  330. {
  331. raftpb.ConfChange{
  332. Type: raftpb.ConfChangeAddNode,
  333. NodeID: 5,
  334. Context: ctx,
  335. },
  336. ErrPeerURLexists,
  337. },
  338. {
  339. raftpb.ConfChange{
  340. Type: raftpb.ConfChangeRemoveNode,
  341. NodeID: 5,
  342. },
  343. ErrIDNotFound,
  344. },
  345. {
  346. raftpb.ConfChange{
  347. Type: raftpb.ConfChangeAddNode,
  348. NodeID: 5,
  349. Context: ctx5,
  350. },
  351. nil,
  352. },
  353. {
  354. raftpb.ConfChange{
  355. Type: raftpb.ConfChangeUpdateNode,
  356. NodeID: 5,
  357. Context: ctx,
  358. },
  359. ErrIDNotFound,
  360. },
  361. // try to change the peer url of 2 to the peer url of 3
  362. {
  363. raftpb.ConfChange{
  364. Type: raftpb.ConfChangeUpdateNode,
  365. NodeID: 2,
  366. Context: ctx2to3,
  367. },
  368. ErrPeerURLexists,
  369. },
  370. {
  371. raftpb.ConfChange{
  372. Type: raftpb.ConfChangeUpdateNode,
  373. NodeID: 2,
  374. Context: ctx2to5,
  375. },
  376. nil,
  377. },
  378. {
  379. raftpb.ConfChange{
  380. Type: raftpb.ConfChangeAddNode,
  381. NodeID: 3,
  382. Context: ctx3,
  383. },
  384. ErrMemberNotLearner,
  385. },
  386. {
  387. raftpb.ConfChange{
  388. Type: raftpb.ConfChangeAddNode,
  389. NodeID: 6,
  390. Context: ctx6,
  391. },
  392. ErrIDNotFound,
  393. },
  394. }
  395. for i, tt := range tests {
  396. err := cl.ValidateConfigurationChange(tt.cc)
  397. if err != tt.werr {
  398. t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
  399. }
  400. }
  401. }
  402. func TestClusterGenID(t *testing.T) {
  403. cs := newTestCluster([]*Member{
  404. newTestMember(1, nil, "", nil),
  405. newTestMember(2, nil, "", nil),
  406. })
  407. cs.genID()
  408. if cs.ID() == 0 {
  409. t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
  410. }
  411. previd := cs.ID()
  412. cs.SetStore(mockstore.NewNop())
  413. cs.AddMember(newTestMember(3, nil, "", nil))
  414. cs.genID()
  415. if cs.ID() == previd {
  416. t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
  417. }
  418. }
  419. func TestNodeToMemberBad(t *testing.T) {
  420. tests := []*v2store.NodeExtern{
  421. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  422. {Key: "/1234/strange"},
  423. }},
  424. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  425. {Key: "/1234/raftAttributes", Value: stringp("garbage")},
  426. }},
  427. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  428. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  429. }},
  430. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  431. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  432. {Key: "/1234/strange"},
  433. }},
  434. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  435. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  436. {Key: "/1234/attributes", Value: stringp("garbage")},
  437. }},
  438. {Key: "/1234", Nodes: []*v2store.NodeExtern{
  439. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  440. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  441. {Key: "/1234/strange"},
  442. }},
  443. }
  444. for i, tt := range tests {
  445. if _, err := nodeToMember(tt); err == nil {
  446. t.Errorf("#%d: unexpected nil error", i)
  447. }
  448. }
  449. }
  450. func TestClusterAddMember(t *testing.T) {
  451. st := mockstore.NewRecorder()
  452. c := newTestCluster(nil)
  453. c.SetStore(st)
  454. c.AddMember(newTestMember(1, nil, "node1", nil))
  455. wactions := []testutil.Action{
  456. {
  457. Name: "Create",
  458. Params: []interface{}{
  459. path.Join(StoreMembersPrefix, "1", "raftAttributes"),
  460. false,
  461. `{"peerURLs":null}`,
  462. false,
  463. v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
  464. },
  465. },
  466. }
  467. if g := st.Action(); !reflect.DeepEqual(g, wactions) {
  468. t.Errorf("actions = %v, want %v", g, wactions)
  469. }
  470. }
  471. func TestClusterAddMemberAsLearner(t *testing.T) {
  472. st := mockstore.NewRecorder()
  473. c := newTestCluster(nil)
  474. c.SetStore(st)
  475. c.AddMember(newTestMemberAsLearner(1, nil, "node1", nil))
  476. wactions := []testutil.Action{
  477. {
  478. Name: "Create",
  479. Params: []interface{}{
  480. path.Join(StoreMembersPrefix, "1", "raftAttributes"),
  481. false,
  482. `{"peerURLs":null,"isLearner":true}`,
  483. false,
  484. v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
  485. },
  486. },
  487. }
  488. if g := st.Action(); !reflect.DeepEqual(g, wactions) {
  489. t.Errorf("actions = %v, want %v", g, wactions)
  490. }
  491. }
  492. func TestClusterMembers(t *testing.T) {
  493. cls := &RaftCluster{
  494. members: map[types.ID]*Member{
  495. 1: {ID: 1},
  496. 20: {ID: 20},
  497. 100: {ID: 100},
  498. 5: {ID: 5},
  499. 50: {ID: 50},
  500. },
  501. }
  502. w := []*Member{
  503. {ID: 1},
  504. {ID: 5},
  505. {ID: 20},
  506. {ID: 50},
  507. {ID: 100},
  508. }
  509. if g := cls.Members(); !reflect.DeepEqual(g, w) {
  510. t.Fatalf("Members()=%#v, want %#v", g, w)
  511. }
  512. }
  513. func TestClusterRemoveMember(t *testing.T) {
  514. st := mockstore.NewRecorder()
  515. c := newTestCluster(nil)
  516. c.SetStore(st)
  517. c.RemoveMember(1)
  518. wactions := []testutil.Action{
  519. {Name: "Delete", Params: []interface{}{MemberStoreKey(1), true, true}},
  520. {Name: "Create", Params: []interface{}{RemovedMemberStoreKey(1), false, "", false, v2store.TTLOptionSet{ExpireTime: v2store.Permanent}}},
  521. }
  522. if !reflect.DeepEqual(st.Action(), wactions) {
  523. t.Errorf("actions = %v, want %v", st.Action(), wactions)
  524. }
  525. }
  526. func TestClusterUpdateAttributes(t *testing.T) {
  527. name := "etcd"
  528. clientURLs := []string{"http://127.0.0.1:4001"}
  529. tests := []struct {
  530. mems []*Member
  531. removed map[types.ID]bool
  532. wmems []*Member
  533. }{
  534. // update attributes of existing member
  535. {
  536. []*Member{
  537. newTestMember(1, nil, "", nil),
  538. },
  539. nil,
  540. []*Member{
  541. newTestMember(1, nil, name, clientURLs),
  542. },
  543. },
  544. // update attributes of removed member
  545. {
  546. nil,
  547. map[types.ID]bool{types.ID(1): true},
  548. nil,
  549. },
  550. }
  551. for i, tt := range tests {
  552. c := newTestCluster(tt.mems)
  553. c.removed = tt.removed
  554. c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs})
  555. if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
  556. t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
  557. }
  558. }
  559. }
  560. func TestNodeToMember(t *testing.T) {
  561. n := &v2store.NodeExtern{Key: "/1234", Nodes: []*v2store.NodeExtern{
  562. {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
  563. {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
  564. }}
  565. wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
  566. m, err := nodeToMember(n)
  567. if err != nil {
  568. t.Fatalf("unexpected nodeToMember error: %v", err)
  569. }
  570. if !reflect.DeepEqual(m, wm) {
  571. t.Errorf("member = %+v, want %+v", m, wm)
  572. }
  573. }
  574. func newTestCluster(membs []*Member) *RaftCluster {
  575. c := &RaftCluster{lg: zap.NewExample(), members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
  576. for _, m := range membs {
  577. c.members[m.ID] = m
  578. }
  579. return c
  580. }
  581. func stringp(s string) *string { return &s }
  582. func TestIsReadyToAddVotingMember(t *testing.T) {
  583. tests := []struct {
  584. members []*Member
  585. want bool
  586. }{
  587. {
  588. // 0/3 members ready, should fail
  589. []*Member{
  590. newTestMember(1, nil, "", nil),
  591. newTestMember(2, nil, "", nil),
  592. newTestMember(3, nil, "", nil),
  593. },
  594. false,
  595. },
  596. {
  597. // 1/2 members ready, should fail
  598. []*Member{
  599. newTestMember(1, nil, "1", nil),
  600. newTestMember(2, nil, "", nil),
  601. },
  602. false,
  603. },
  604. {
  605. // 1/3 members ready, should fail
  606. []*Member{
  607. newTestMember(1, nil, "1", nil),
  608. newTestMember(2, nil, "", nil),
  609. newTestMember(3, nil, "", nil),
  610. },
  611. false,
  612. },
  613. {
  614. // 1/1 members ready, should succeed (special case of 1-member cluster for recovery)
  615. []*Member{
  616. newTestMember(1, nil, "1", nil),
  617. },
  618. true,
  619. },
  620. {
  621. // 2/3 members ready, should fail
  622. []*Member{
  623. newTestMember(1, nil, "1", nil),
  624. newTestMember(2, nil, "2", nil),
  625. newTestMember(3, nil, "", nil),
  626. },
  627. false,
  628. },
  629. {
  630. // 3/3 members ready, should be fine to add one member and retain quorum
  631. []*Member{
  632. newTestMember(1, nil, "1", nil),
  633. newTestMember(2, nil, "2", nil),
  634. newTestMember(3, nil, "3", nil),
  635. },
  636. true,
  637. },
  638. {
  639. // 3/4 members ready, should be fine to add one member and retain quorum
  640. []*Member{
  641. newTestMember(1, nil, "1", nil),
  642. newTestMember(2, nil, "2", nil),
  643. newTestMember(3, nil, "3", nil),
  644. newTestMember(4, nil, "", nil),
  645. },
  646. true,
  647. },
  648. {
  649. // empty cluster, it is impossible but should fail
  650. []*Member{},
  651. false,
  652. },
  653. {
  654. // 2 voting members ready in cluster with 2 voting members and 2 unstarted learner member, should succeed
  655. // (the status of learner members does not affect the readiness of adding voting member)
  656. []*Member{
  657. newTestMember(1, nil, "1", nil),
  658. newTestMember(2, nil, "2", nil),
  659. newTestMemberAsLearner(3, nil, "", nil),
  660. newTestMemberAsLearner(4, nil, "", nil),
  661. },
  662. true,
  663. },
  664. {
  665. // 1 voting member ready in cluster with 2 voting members and 2 ready learner member, should fail
  666. // (the status of learner members does not affect the readiness of adding voting member)
  667. []*Member{
  668. newTestMember(1, nil, "1", nil),
  669. newTestMember(2, nil, "", nil),
  670. newTestMemberAsLearner(3, nil, "3", nil),
  671. newTestMemberAsLearner(4, nil, "4", nil),
  672. },
  673. false,
  674. },
  675. }
  676. for i, tt := range tests {
  677. c := newTestCluster(tt.members)
  678. if got := c.IsReadyToAddVotingMember(); got != tt.want {
  679. t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
  680. }
  681. }
  682. }
  683. func TestIsReadyToRemoveVotingMember(t *testing.T) {
  684. tests := []struct {
  685. members []*Member
  686. removeID uint64
  687. want bool
  688. }{
  689. {
  690. // 1/1 members ready, should fail
  691. []*Member{
  692. newTestMember(1, nil, "1", nil),
  693. },
  694. 1,
  695. false,
  696. },
  697. {
  698. // 0/3 members ready, should fail
  699. []*Member{
  700. newTestMember(1, nil, "", nil),
  701. newTestMember(2, nil, "", nil),
  702. newTestMember(3, nil, "", nil),
  703. },
  704. 1,
  705. false,
  706. },
  707. {
  708. // 1/2 members ready, should be fine to remove unstarted member
  709. // (isReadyToRemoveMember() logic should return success, but operation itself would fail)
  710. []*Member{
  711. newTestMember(1, nil, "1", nil),
  712. newTestMember(2, nil, "", nil),
  713. },
  714. 2,
  715. true,
  716. },
  717. {
  718. // 2/3 members ready, should fail
  719. []*Member{
  720. newTestMember(1, nil, "1", nil),
  721. newTestMember(2, nil, "2", nil),
  722. newTestMember(3, nil, "", nil),
  723. },
  724. 2,
  725. false,
  726. },
  727. {
  728. // 3/3 members ready, should be fine to remove one member and retain quorum
  729. []*Member{
  730. newTestMember(1, nil, "1", nil),
  731. newTestMember(2, nil, "2", nil),
  732. newTestMember(3, nil, "3", nil),
  733. },
  734. 3,
  735. true,
  736. },
  737. {
  738. // 3/4 members ready, should be fine to remove one member
  739. []*Member{
  740. newTestMember(1, nil, "1", nil),
  741. newTestMember(2, nil, "2", nil),
  742. newTestMember(3, nil, "3", nil),
  743. newTestMember(4, nil, "", nil),
  744. },
  745. 3,
  746. true,
  747. },
  748. {
  749. // 3/4 members ready, should be fine to remove unstarted member
  750. []*Member{
  751. newTestMember(1, nil, "1", nil),
  752. newTestMember(2, nil, "2", nil),
  753. newTestMember(3, nil, "3", nil),
  754. newTestMember(4, nil, "", nil),
  755. },
  756. 4,
  757. true,
  758. },
  759. {
  760. // 1 voting members ready in cluster with 1 voting member and 1 ready learner,
  761. // removing voting member should fail
  762. // (the status of learner members does not affect the readiness of removing voting member)
  763. []*Member{
  764. newTestMember(1, nil, "1", nil),
  765. newTestMemberAsLearner(2, nil, "2", nil),
  766. },
  767. 1,
  768. false,
  769. },
  770. {
  771. // 1 voting members ready in cluster with 2 voting member and 1 ready learner,
  772. // removing ready voting member should fail
  773. // (the status of learner members does not affect the readiness of removing voting member)
  774. []*Member{
  775. newTestMember(1, nil, "1", nil),
  776. newTestMember(2, nil, "", nil),
  777. newTestMemberAsLearner(3, nil, "3", nil),
  778. },
  779. 1,
  780. false,
  781. },
  782. {
  783. // 1 voting members ready in cluster with 2 voting member and 1 ready learner,
  784. // removing unstarted voting member should be fine. (Actual operation will fail)
  785. // (the status of learner members does not affect the readiness of removing voting member)
  786. []*Member{
  787. newTestMember(1, nil, "1", nil),
  788. newTestMember(2, nil, "", nil),
  789. newTestMemberAsLearner(3, nil, "3", nil),
  790. },
  791. 2,
  792. true,
  793. },
  794. {
  795. // 1 voting members ready in cluster with 2 voting member and 1 unstarted learner,
  796. // removing not-ready voting member should be fine. (Actual operation will fail)
  797. // (the status of learner members does not affect the readiness of removing voting member)
  798. []*Member{
  799. newTestMember(1, nil, "1", nil),
  800. newTestMember(2, nil, "", nil),
  801. newTestMemberAsLearner(3, nil, "", nil),
  802. },
  803. 2,
  804. true,
  805. },
  806. }
  807. for i, tt := range tests {
  808. c := newTestCluster(tt.members)
  809. if got := c.IsReadyToRemoveVotingMember(tt.removeID); got != tt.want {
  810. t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
  811. }
  812. }
  813. }