1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package membership
16
17 import (
18 "encoding/json"
19 "fmt"
20 "path"
21 "reflect"
22 "testing"
23 "time"
24
25 "github.com/coreos/go-semver/semver"
26 "github.com/stretchr/testify/assert"
27 betesting "go.etcd.io/etcd/server/v3/mvcc/backend/testing"
28 "go.uber.org/zap"
29 "go.uber.org/zap/zaptest"
30
31 "go.etcd.io/etcd/client/pkg/v3/testutil"
32 "go.etcd.io/etcd/client/pkg/v3/types"
33 "go.etcd.io/etcd/raft/v3/raftpb"
34 "go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
35 "go.etcd.io/etcd/server/v3/mock/mockstore"
36 )
37
38 func TestClusterMember(t *testing.T) {
39 membs := []*Member{
40 newTestMember(1, nil, "node1", nil),
41 newTestMember(2, nil, "node2", nil),
42 }
43 tests := []struct {
44 id types.ID
45 match bool
46 }{
47 {1, true},
48 {2, true},
49 {3, false},
50 }
51 for i, tt := range tests {
52 c := newTestCluster(t, membs)
53 m := c.Member(tt.id)
54 if g := m != nil; g != tt.match {
55 t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
56 }
57 if m != nil && m.ID != tt.id {
58 t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
59 }
60 }
61 }
62
63 func TestClusterMemberByName(t *testing.T) {
64 membs := []*Member{
65 newTestMember(1, nil, "node1", nil),
66 newTestMember(2, nil, "node2", nil),
67 }
68 tests := []struct {
69 name string
70 match bool
71 }{
72 {"node1", true},
73 {"node2", true},
74 {"node3", false},
75 }
76 for i, tt := range tests {
77 c := newTestCluster(t, membs)
78 m := c.MemberByName(tt.name)
79 if g := m != nil; g != tt.match {
80 t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
81 }
82 if m != nil && m.Name != tt.name {
83 t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
84 }
85 }
86 }
87
88 func TestClusterMemberIDs(t *testing.T) {
89 c := newTestCluster(t, []*Member{
90 newTestMember(1, nil, "", nil),
91 newTestMember(4, nil, "", nil),
92 newTestMember(100, nil, "", nil),
93 })
94 w := []types.ID{1, 4, 100}
95 g := c.MemberIDs()
96 if !reflect.DeepEqual(w, g) {
97 t.Errorf("IDs = %+v, want %+v", g, w)
98 }
99 }
100
101 func TestClusterPeerURLs(t *testing.T) {
102 tests := []struct {
103 mems []*Member
104 wurls []string
105 }{
106
107 {
108 mems: []*Member{
109 newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
110 },
111 wurls: []string{"http://192.0.2.1"},
112 },
113
114
115 {
116 mems: []*Member{
117 newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
118 },
119 wurls: []string{"http://192.0.2.1:8001"},
120 },
121
122
123 {
124 mems: []*Member{
125 newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
126 newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
127 newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
128 },
129 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"},
130 },
131
132
133 {
134 mems: []*Member{},
135 wurls: []string{},
136 },
137
138
139 {
140 mems: []*Member{
141 newTestMember(3, []string{}, "", nil),
142 },
143 wurls: []string{},
144 },
145 }
146
147 for i, tt := range tests {
148 c := newTestCluster(t, tt.mems)
149 urls := c.PeerURLs()
150 if !reflect.DeepEqual(urls, tt.wurls) {
151 t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
152 }
153 }
154 }
155
156 func TestClusterClientURLs(t *testing.T) {
157 tests := []struct {
158 mems []*Member
159 wurls []string
160 }{
161
162 {
163 mems: []*Member{
164 newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
165 },
166 wurls: []string{"http://192.0.2.1"},
167 },
168
169
170 {
171 mems: []*Member{
172 newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
173 },
174 wurls: []string{"http://192.0.2.1:8001"},
175 },
176
177
178 {
179 mems: []*Member{
180 newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
181 newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
182 newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
183 },
184 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"},
185 },
186
187
188 {
189 mems: []*Member{},
190 wurls: []string{},
191 },
192
193
194 {
195 mems: []*Member{
196 newTestMember(3, nil, "", []string{}),
197 },
198 wurls: []string{},
199 },
200 }
201
202 for i, tt := range tests {
203 c := newTestCluster(t, tt.mems)
204 urls := c.ClientURLs()
205 if !reflect.DeepEqual(urls, tt.wurls) {
206 t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
207 }
208 }
209 }
210
211 func TestClusterValidateAndAssignIDsBad(t *testing.T) {
212 tests := []struct {
213 clmembs []*Member
214 membs []*Member
215 }{
216 {
217
218 []*Member{
219 newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
220 },
221 []*Member{},
222 },
223 {
224
225 []*Member{
226 newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
227 },
228 []*Member{
229 newTestMember(1, []string{"http://127.0.0.1:4001"}, "", nil),
230 },
231 },
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(1, []string{"http://127.0.0.1:2379"}, "", nil),
240 newTestMember(2, []string{"http://127.0.0.2:4001"}, "", nil),
241 },
242 },
243 }
244 for i, tt := range tests {
245 ecl := newTestCluster(t, tt.clmembs)
246 lcl := newTestCluster(t, tt.membs)
247 if err := ValidateClusterAndAssignIDs(zap.NewExample(), lcl, ecl); err == nil {
248 t.Errorf("#%d: unexpected update success", i)
249 }
250 }
251 }
252
253 func TestClusterValidateAndAssignIDs(t *testing.T) {
254 tests := []struct {
255 clmembs []*Member
256 membs []*Member
257 wids []types.ID
258 }{
259 {
260 []*Member{
261 newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
262 newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
263 },
264 []*Member{
265 newTestMember(3, []string{"http://127.0.0.1:2379"}, "", nil),
266 newTestMember(4, []string{"http://127.0.0.2:2379"}, "", nil),
267 },
268 []types.ID{3, 4},
269 },
270 }
271 for i, tt := range tests {
272 lcl := newTestCluster(t, tt.clmembs)
273 ecl := newTestCluster(t, tt.membs)
274 if err := ValidateClusterAndAssignIDs(zap.NewExample(), lcl, ecl); err != nil {
275 t.Errorf("#%d: unexpect update error: %v", i, err)
276 }
277 if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
278 t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
279 }
280 }
281 }
282
283 func TestClusterValidateConfigurationChange(t *testing.T) {
284 cl := NewCluster(zaptest.NewLogger(t))
285 cl.SetStore(v2store.New())
286 for i := 1; i <= 4; i++ {
287 attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", i)}}
288 cl.AddMember(&Member{ID: types.ID(i), RaftAttributes: attr}, true)
289 }
290 cl.RemoveMember(4, true)
291
292 attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
293 ctx, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
294 if err != nil {
295 t.Fatal(err)
296 }
297
298 attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
299 ctx1, err := json.Marshal(&Member{ID: types.ID(1), RaftAttributes: attr})
300 if err != nil {
301 t.Fatal(err)
302 }
303
304 attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
305 ctx5, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
306 if err != nil {
307 t.Fatal(err)
308 }
309
310 attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 3)}}
311 ctx2to3, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
312 if err != nil {
313 t.Fatal(err)
314 }
315
316 attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
317 ctx2to5, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
318 if err != nil {
319 t.Fatal(err)
320 }
321
322 ctx3, err := json.Marshal(&ConfigChangeContext{Member: Member{ID: types.ID(3), RaftAttributes: attr}, IsPromote: true})
323 if err != nil {
324 t.Fatal(err)
325 }
326
327 ctx6, err := json.Marshal(&ConfigChangeContext{Member: Member{ID: types.ID(6), RaftAttributes: attr}, IsPromote: true})
328 if err != nil {
329 t.Fatal(err)
330 }
331
332 tests := []struct {
333 cc raftpb.ConfChange
334 werr error
335 }{
336 {
337 raftpb.ConfChange{
338 Type: raftpb.ConfChangeRemoveNode,
339 NodeID: 3,
340 },
341 nil,
342 },
343 {
344 raftpb.ConfChange{
345 Type: raftpb.ConfChangeAddNode,
346 NodeID: 4,
347 },
348 ErrIDRemoved,
349 },
350 {
351 raftpb.ConfChange{
352 Type: raftpb.ConfChangeRemoveNode,
353 NodeID: 4,
354 },
355 ErrIDRemoved,
356 },
357 {
358 raftpb.ConfChange{
359 Type: raftpb.ConfChangeAddNode,
360 NodeID: 1,
361 Context: ctx1,
362 },
363 ErrIDExists,
364 },
365 {
366 raftpb.ConfChange{
367 Type: raftpb.ConfChangeAddNode,
368 NodeID: 5,
369 Context: ctx,
370 },
371 ErrPeerURLexists,
372 },
373 {
374 raftpb.ConfChange{
375 Type: raftpb.ConfChangeRemoveNode,
376 NodeID: 5,
377 },
378 ErrIDNotFound,
379 },
380 {
381 raftpb.ConfChange{
382 Type: raftpb.ConfChangeAddNode,
383 NodeID: 5,
384 Context: ctx5,
385 },
386 nil,
387 },
388 {
389 raftpb.ConfChange{
390 Type: raftpb.ConfChangeUpdateNode,
391 NodeID: 5,
392 Context: ctx,
393 },
394 ErrIDNotFound,
395 },
396
397 {
398 raftpb.ConfChange{
399 Type: raftpb.ConfChangeUpdateNode,
400 NodeID: 2,
401 Context: ctx2to3,
402 },
403 ErrPeerURLexists,
404 },
405 {
406 raftpb.ConfChange{
407 Type: raftpb.ConfChangeUpdateNode,
408 NodeID: 2,
409 Context: ctx2to5,
410 },
411 nil,
412 },
413 {
414 raftpb.ConfChange{
415 Type: raftpb.ConfChangeAddNode,
416 NodeID: 3,
417 Context: ctx3,
418 },
419 ErrMemberNotLearner,
420 },
421 {
422 raftpb.ConfChange{
423 Type: raftpb.ConfChangeAddNode,
424 NodeID: 6,
425 Context: ctx6,
426 },
427 ErrIDNotFound,
428 },
429 }
430 for i, tt := range tests {
431 err := cl.ValidateConfigurationChange(tt.cc)
432 if err != tt.werr {
433 t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
434 }
435 }
436 }
437
438 func TestClusterGenID(t *testing.T) {
439 cs := newTestCluster(t, []*Member{
440 newTestMember(1, nil, "", nil),
441 newTestMember(2, nil, "", nil),
442 })
443
444 cs.genID()
445 if cs.ID() == 0 {
446 t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
447 }
448 previd := cs.ID()
449
450 cs.SetStore(mockstore.NewNop())
451 cs.AddMember(newTestMember(3, nil, "", nil), true)
452 cs.genID()
453 if cs.ID() == previd {
454 t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
455 }
456 }
457
458 func TestNodeToMemberBad(t *testing.T) {
459 tests := []*v2store.NodeExtern{
460 {Key: "/1234", Nodes: []*v2store.NodeExtern{
461 {Key: "/1234/strange"},
462 }},
463 {Key: "/1234", Nodes: []*v2store.NodeExtern{
464 {Key: "/1234/raftAttributes", Value: stringp("garbage")},
465 }},
466 {Key: "/1234", Nodes: []*v2store.NodeExtern{
467 {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
468 }},
469 {Key: "/1234", Nodes: []*v2store.NodeExtern{
470 {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
471 {Key: "/1234/strange"},
472 }},
473 {Key: "/1234", Nodes: []*v2store.NodeExtern{
474 {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
475 {Key: "/1234/attributes", Value: stringp("garbage")},
476 }},
477 {Key: "/1234", Nodes: []*v2store.NodeExtern{
478 {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
479 {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
480 {Key: "/1234/strange"},
481 }},
482 }
483 for i, tt := range tests {
484 if _, err := nodeToMember(zap.NewExample(), tt); err == nil {
485 t.Errorf("#%d: unexpected nil error", i)
486 }
487 }
488 }
489
490 func TestClusterAddMember(t *testing.T) {
491 st := mockstore.NewRecorder()
492 c := newTestCluster(t, nil)
493 c.SetStore(st)
494 c.AddMember(newTestMember(1, nil, "node1", nil), true)
495
496 wactions := []testutil.Action{
497 {
498 Name: "Create",
499 Params: []interface{}{
500 path.Join(StoreMembersPrefix, "1", "raftAttributes"),
501 false,
502 `{"peerURLs":null}`,
503 false,
504 v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
505 },
506 },
507 }
508 if g := st.Action(); !reflect.DeepEqual(g, wactions) {
509 t.Errorf("actions = %v, want %v", g, wactions)
510 }
511 }
512
513 func TestClusterAddMemberAsLearner(t *testing.T) {
514 st := mockstore.NewRecorder()
515 c := newTestCluster(t, nil)
516 c.SetStore(st)
517 c.AddMember(newTestMemberAsLearner(1, nil, "node1", nil), true)
518
519 wactions := []testutil.Action{
520 {
521 Name: "Create",
522 Params: []interface{}{
523 path.Join(StoreMembersPrefix, "1", "raftAttributes"),
524 false,
525 `{"peerURLs":null,"isLearner":true}`,
526 false,
527 v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
528 },
529 },
530 }
531 if g := st.Action(); !reflect.DeepEqual(g, wactions) {
532 t.Errorf("actions = %v, want %v", g, wactions)
533 }
534 }
535
536 func TestClusterMembers(t *testing.T) {
537 cls := newTestCluster(t, []*Member{
538 {ID: 1},
539 {ID: 20},
540 {ID: 100},
541 {ID: 5},
542 {ID: 50},
543 })
544 w := []*Member{
545 {ID: 1},
546 {ID: 5},
547 {ID: 20},
548 {ID: 50},
549 {ID: 100},
550 }
551 if g := cls.Members(); !reflect.DeepEqual(g, w) {
552 t.Fatalf("Members()=%#v, want %#v", g, w)
553 }
554 }
555
556 func TestClusterRemoveMember(t *testing.T) {
557 st := mockstore.NewRecorder()
558 c := newTestCluster(t, nil)
559 c.SetStore(st)
560 c.RemoveMember(1, true)
561
562 wactions := []testutil.Action{
563 {Name: "Delete", Params: []interface{}{MemberStoreKey(1), true, true}},
564 {Name: "Create", Params: []interface{}{RemovedMemberStoreKey(1), false, "", false, v2store.TTLOptionSet{ExpireTime: v2store.Permanent}}},
565 }
566 if !reflect.DeepEqual(st.Action(), wactions) {
567 t.Errorf("actions = %v, want %v", st.Action(), wactions)
568 }
569 }
570
571 func TestClusterUpdateAttributes(t *testing.T) {
572 name := "etcd"
573 clientURLs := []string{"http://127.0.0.1:4001"}
574 tests := []struct {
575 mems []*Member
576 removed map[types.ID]bool
577 wmems []*Member
578 }{
579
580 {
581 []*Member{
582 newTestMember(1, nil, "", nil),
583 },
584 nil,
585 []*Member{
586 newTestMember(1, nil, name, clientURLs),
587 },
588 },
589
590 {
591 nil,
592 map[types.ID]bool{types.ID(1): true},
593 nil,
594 },
595 }
596 for i, tt := range tests {
597 c := newTestCluster(t, tt.mems)
598 c.removed = tt.removed
599
600 c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs}, true)
601 if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
602 t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
603 }
604 }
605 }
606
607 func TestNodeToMember(t *testing.T) {
608 n := &v2store.NodeExtern{Key: "/1234", Nodes: []*v2store.NodeExtern{
609 {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
610 {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
611 }}
612 wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
613 m, err := nodeToMember(zap.NewExample(), n)
614 if err != nil {
615 t.Fatalf("unexpected nodeToMember error: %v", err)
616 }
617 if !reflect.DeepEqual(m, wm) {
618 t.Errorf("member = %+v, want %+v", m, wm)
619 }
620 }
621
622 func newTestCluster(t testing.TB, membs []*Member) *RaftCluster {
623 c := &RaftCluster{lg: zaptest.NewLogger(t), 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
630 func stringp(s string) *string { return &s }
631
632 func TestIsReadyToAddVotingMember(t *testing.T) {
633 tests := []struct {
634 members []*Member
635 want bool
636 }{
637 {
638
639 []*Member{
640 newTestMember(1, nil, "", nil),
641 newTestMember(2, nil, "", nil),
642 newTestMember(3, nil, "", nil),
643 },
644 false,
645 },
646 {
647
648 []*Member{
649 newTestMember(1, nil, "1", nil),
650 newTestMember(2, nil, "", nil),
651 },
652 false,
653 },
654 {
655
656 []*Member{
657 newTestMember(1, nil, "1", nil),
658 newTestMember(2, nil, "", nil),
659 newTestMember(3, nil, "", nil),
660 },
661 false,
662 },
663 {
664
665 []*Member{
666 newTestMember(1, nil, "1", nil),
667 },
668 true,
669 },
670 {
671
672 []*Member{
673 newTestMember(1, nil, "1", nil),
674 newTestMember(2, nil, "2", nil),
675 newTestMember(3, nil, "", nil),
676 },
677 false,
678 },
679 {
680
681 []*Member{
682 newTestMember(1, nil, "1", nil),
683 newTestMember(2, nil, "2", nil),
684 newTestMember(3, nil, "3", nil),
685 },
686 true,
687 },
688 {
689
690 []*Member{
691 newTestMember(1, nil, "1", nil),
692 newTestMember(2, nil, "2", nil),
693 newTestMember(3, nil, "3", nil),
694 newTestMember(4, nil, "", nil),
695 },
696 true,
697 },
698 {
699
700 []*Member{},
701 false,
702 },
703 {
704
705
706 []*Member{
707 newTestMember(1, nil, "1", nil),
708 newTestMember(2, nil, "2", nil),
709 newTestMemberAsLearner(3, nil, "", nil),
710 newTestMemberAsLearner(4, nil, "", nil),
711 },
712 true,
713 },
714 {
715
716
717 []*Member{
718 newTestMember(1, nil, "1", nil),
719 newTestMember(2, nil, "", nil),
720 newTestMemberAsLearner(3, nil, "3", nil),
721 newTestMemberAsLearner(4, nil, "4", nil),
722 },
723 false,
724 },
725 }
726 for i, tt := range tests {
727 c := newTestCluster(t, tt.members)
728 if got := c.IsReadyToAddVotingMember(); got != tt.want {
729 t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
730 }
731 }
732 }
733
734 func TestIsReadyToRemoveVotingMember(t *testing.T) {
735 tests := []struct {
736 members []*Member
737 removeID uint64
738 want bool
739 }{
740 {
741
742 []*Member{
743 newTestMember(1, nil, "1", nil),
744 },
745 1,
746 false,
747 },
748 {
749
750 []*Member{
751 newTestMember(1, nil, "", nil),
752 newTestMember(2, nil, "", nil),
753 newTestMember(3, nil, "", nil),
754 },
755 1,
756 false,
757 },
758 {
759
760
761 []*Member{
762 newTestMember(1, nil, "1", nil),
763 newTestMember(2, nil, "", nil),
764 },
765 2,
766 true,
767 },
768 {
769
770 []*Member{
771 newTestMember(1, nil, "1", nil),
772 newTestMember(2, nil, "2", nil),
773 newTestMember(3, nil, "", nil),
774 },
775 2,
776 false,
777 },
778 {
779
780 []*Member{
781 newTestMember(1, nil, "1", nil),
782 newTestMember(2, nil, "2", nil),
783 newTestMember(3, nil, "3", nil),
784 },
785 3,
786 true,
787 },
788 {
789
790 []*Member{
791 newTestMember(1, nil, "1", nil),
792 newTestMember(2, nil, "2", nil),
793 newTestMember(3, nil, "3", nil),
794 newTestMember(4, nil, "", nil),
795 },
796 3,
797 true,
798 },
799 {
800
801 []*Member{
802 newTestMember(1, nil, "1", nil),
803 newTestMember(2, nil, "2", nil),
804 newTestMember(3, nil, "3", nil),
805 newTestMember(4, nil, "", nil),
806 },
807 4,
808 true,
809 },
810 {
811
812
813
814 []*Member{
815 newTestMember(1, nil, "1", nil),
816 newTestMemberAsLearner(2, nil, "2", nil),
817 },
818 1,
819 false,
820 },
821 {
822
823
824
825 []*Member{
826 newTestMember(1, nil, "1", nil),
827 newTestMember(2, nil, "", nil),
828 newTestMemberAsLearner(3, nil, "3", nil),
829 },
830 1,
831 false,
832 },
833 {
834
835
836
837 []*Member{
838 newTestMember(1, nil, "1", nil),
839 newTestMember(2, nil, "", nil),
840 newTestMemberAsLearner(3, nil, "3", nil),
841 },
842 2,
843 true,
844 },
845 {
846
847
848
849 []*Member{
850 newTestMember(1, nil, "1", nil),
851 newTestMember(2, nil, "", nil),
852 newTestMemberAsLearner(3, nil, "", nil),
853 },
854 2,
855 true,
856 },
857 }
858 for i, tt := range tests {
859 c := newTestCluster(t, tt.members)
860 if got := c.IsReadyToRemoveVotingMember(tt.removeID); got != tt.want {
861 t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
862 }
863 }
864 }
865
866 func TestIsReadyToPromoteMember(t *testing.T) {
867 tests := []struct {
868 members []*Member
869 promoteID uint64
870 want bool
871 }{
872 {
873
874 []*Member{
875 newTestMember(1, nil, "1", nil),
876 newTestMemberAsLearner(2, nil, "2", nil),
877 },
878 2,
879 true,
880 },
881 {
882
883 []*Member{
884 newTestMember(1, nil, "", nil),
885 newTestMemberAsLearner(2, nil, "2", nil),
886 },
887 2,
888 false,
889 },
890 {
891
892 []*Member{
893 newTestMember(1, nil, "1", nil),
894 newTestMember(2, nil, "2", nil),
895 newTestMemberAsLearner(3, nil, "3", nil),
896 },
897 3,
898 true,
899 },
900 {
901
902 []*Member{
903 newTestMember(1, nil, "1", nil),
904 newTestMember(2, nil, "", nil),
905 newTestMemberAsLearner(3, nil, "3", nil),
906 },
907 3,
908 true,
909 },
910 {
911
912 []*Member{
913 newTestMember(1, nil, "1", nil),
914 newTestMember(2, nil, "", nil),
915 newTestMember(3, nil, "", nil),
916 newTestMemberAsLearner(4, nil, "4", nil),
917 },
918 4,
919 false,
920 },
921 {
922
923 []*Member{
924 newTestMember(1, nil, "1", nil),
925 newTestMember(2, nil, "2", nil),
926 newTestMember(3, nil, "", nil),
927 newTestMemberAsLearner(4, nil, "4", nil),
928 },
929 4,
930 true,
931 },
932 {
933
934 []*Member{
935 newTestMember(1, nil, "1", nil),
936 newTestMember(2, nil, "2", nil),
937 newTestMember(3, nil, "", nil),
938 newTestMember(4, nil, "", nil),
939 newTestMemberAsLearner(5, nil, "5", nil),
940 },
941 5,
942 true,
943 },
944 }
945 for i, tt := range tests {
946 c := newTestCluster(t, tt.members)
947 if got := c.IsReadyToPromoteMember(tt.promoteID); got != tt.want {
948 t.Errorf("%d: isReadyToPromoteMember returned %t, want %t", i, got, tt.want)
949 }
950 }
951 }
952
953 func TestIsVersionChangable(t *testing.T) {
954 v0 := semver.Must(semver.NewVersion("2.4.0"))
955 v1 := semver.Must(semver.NewVersion("3.4.0"))
956 v2 := semver.Must(semver.NewVersion("3.5.0"))
957 v3 := semver.Must(semver.NewVersion("3.5.1"))
958 v4 := semver.Must(semver.NewVersion("3.6.0"))
959
960 tests := []struct {
961 name string
962 currentVersion *semver.Version
963 localVersion *semver.Version
964 expectedResult bool
965 }{
966 {
967 name: "When local version is one minor lower than cluster version",
968 currentVersion: v2,
969 localVersion: v1,
970 expectedResult: true,
971 },
972 {
973 name: "When local version is one minor and one patch lower than cluster version",
974 currentVersion: v3,
975 localVersion: v1,
976 expectedResult: true,
977 },
978 {
979 name: "When local version is one minor higher than cluster version",
980 currentVersion: v1,
981 localVersion: v2,
982 expectedResult: true,
983 },
984 {
985 name: "When local version is two minor higher than cluster version",
986 currentVersion: v1,
987 localVersion: v4,
988 expectedResult: true,
989 },
990 {
991 name: "When local version is one major higher than cluster version",
992 currentVersion: v0,
993 localVersion: v1,
994 expectedResult: false,
995 },
996 {
997 name: "When local version is equal to cluster version",
998 currentVersion: v1,
999 localVersion: v1,
1000 expectedResult: false,
1001 },
1002 {
1003 name: "When local version is one patch higher than cluster version",
1004 currentVersion: v2,
1005 localVersion: v3,
1006 expectedResult: false,
1007 },
1008 {
1009 name: "When local version is two minor lower than cluster version",
1010 currentVersion: v4,
1011 localVersion: v1,
1012 expectedResult: false,
1013 },
1014 }
1015
1016 for _, tt := range tests {
1017 t.Run(tt.name, func(t *testing.T) {
1018 if ret := IsValidVersionChange(tt.currentVersion, tt.localVersion); ret != tt.expectedResult {
1019 t.Errorf("Expected %v; Got %v", tt.expectedResult, ret)
1020 }
1021 })
1022 }
1023 }
1024
1025 func TestAddMemberSyncsBackendAndStoreV2(t *testing.T) {
1026 now := time.Now()
1027 alice := NewMember("", nil, "alice", &now)
1028
1029 tcs := []struct {
1030 name string
1031
1032 storeV2Nil bool
1033 backendNil bool
1034 storeV2Members []*Member
1035 backendMembers []*Member
1036
1037 expectPanics bool
1038 expectMembers map[types.ID]*Member
1039 }{
1040 {
1041 name: "Adding new member should succeed",
1042 },
1043 {
1044 name: "Adding member should succeed if it was only in storeV2",
1045 storeV2Members: []*Member{alice},
1046 },
1047 {
1048 name: "Adding member should succeed if it was only in backend",
1049 backendMembers: []*Member{alice},
1050 },
1051 {
1052 name: "Adding member should fail if it exists in both",
1053 storeV2Members: []*Member{alice},
1054 backendMembers: []*Member{alice},
1055 expectPanics: true,
1056 },
1057 {
1058 name: "Adding member should fail if it exists in storeV2 and backend is nil",
1059 storeV2Members: []*Member{alice},
1060 backendNil: true,
1061 expectPanics: true,
1062 },
1063 {
1064 name: "Adding member should succeed if it exists in backend and storageV2 is nil",
1065 storeV2Nil: true,
1066 backendMembers: []*Member{alice},
1067 },
1068 {
1069 name: "Adding new member should succeed if backend is nil",
1070 storeV2Members: []*Member{},
1071 backendNil: true,
1072 },
1073 {
1074 name: "Adding new member should fail if storageV2 is nil",
1075 storeV2Nil: true,
1076 backendMembers: []*Member{},
1077 },
1078 }
1079 for _, tc := range tcs {
1080 t.Run(tc.name, func(t *testing.T) {
1081 lg := zaptest.NewLogger(t)
1082 be, _ := betesting.NewDefaultTmpBackend(t)
1083 defer be.Close()
1084 mustCreateBackendBuckets(be)
1085 st := v2store.New()
1086 for _, m := range tc.backendMembers {
1087 unsafeSaveMemberToBackend(lg, be, m)
1088 }
1089 be.ForceCommit()
1090 for _, m := range tc.storeV2Members {
1091 mustSaveMemberToStore(lg, st, m)
1092 }
1093 cluster := NewCluster(lg)
1094 if !tc.backendNil {
1095 cluster.SetBackend(be)
1096 }
1097 if !tc.storeV2Nil {
1098 cluster.SetStore(st)
1099 }
1100 if tc.expectPanics {
1101 assert.Panics(t, func() {
1102 cluster.AddMember(alice, ApplyBoth)
1103 })
1104 } else {
1105 cluster.AddMember(alice, ApplyBoth)
1106 }
1107 if !tc.storeV2Nil {
1108 storeV2Members, _ := membersFromStore(lg, st)
1109 assert.Equal(t, map[types.ID]*Member{alice.ID: alice}, storeV2Members)
1110 }
1111 if !tc.backendNil {
1112 be.ForceCommit()
1113 beMembers, _ := mustReadMembersFromBackend(lg, be)
1114 assert.Equal(t, map[types.ID]*Member{alice.ID: alice}, beMembers)
1115 }
1116 })
1117 }
1118 }
1119
1120 func TestRemoveMemberSyncsBackendAndStoreV2(t *testing.T) {
1121 now := time.Now()
1122 alice := NewMember("", nil, "alice", &now)
1123
1124 tcs := []struct {
1125 name string
1126
1127 storeV2Nil bool
1128 backendNil bool
1129 storeV2Members []*Member
1130 backendMembers []*Member
1131
1132 expectMembers []*Member
1133 expectPanics bool
1134 }{
1135 {
1136 name: "Removing new member should fail",
1137 expectPanics: true,
1138 },
1139 {
1140 name: "Removing member should succeed if it was only in storeV2",
1141 storeV2Members: []*Member{alice},
1142 },
1143 {
1144 name: "Removing member should succeed if it was only in backend",
1145 backendMembers: []*Member{alice},
1146 },
1147 {
1148 name: "Removing member should succeed if it exists in both",
1149 storeV2Members: []*Member{alice},
1150 backendMembers: []*Member{alice},
1151 },
1152 {
1153 name: "Removing new member should fail if backend is nil",
1154 storeV2Members: []*Member{},
1155 backendNil: true,
1156 expectPanics: true,
1157 },
1158 {
1159 name: "Removing new member should succeed if storageV2 is nil",
1160 storeV2Nil: true,
1161 backendMembers: []*Member{},
1162 },
1163 {
1164 name: "Removing member should succeed if it exists in v2storage and backend is nil",
1165 storeV2Members: []*Member{alice},
1166 backendNil: true,
1167 },
1168 {
1169 name: "Removing member should succeed if it exists in backend and storageV2 is nil",
1170 storeV2Nil: true,
1171 backendMembers: []*Member{alice},
1172 },
1173 }
1174 for _, tc := range tcs {
1175 t.Run(tc.name, func(t *testing.T) {
1176 lg := zaptest.NewLogger(t)
1177 be, _ := betesting.NewDefaultTmpBackend(t)
1178 defer be.Close()
1179 mustCreateBackendBuckets(be)
1180 st := v2store.New()
1181 for _, m := range tc.backendMembers {
1182 unsafeSaveMemberToBackend(lg, be, m)
1183 }
1184 be.ForceCommit()
1185 for _, m := range tc.storeV2Members {
1186 mustSaveMemberToStore(lg, st, m)
1187 }
1188 cluster := NewCluster(lg)
1189 if !tc.backendNil {
1190 cluster.SetBackend(be)
1191 }
1192 if !tc.storeV2Nil {
1193 cluster.SetStore(st)
1194 }
1195 if tc.expectPanics {
1196 assert.Panics(t, func() {
1197 cluster.RemoveMember(alice.ID, ApplyBoth)
1198 })
1199 } else {
1200 cluster.RemoveMember(alice.ID, ApplyBoth)
1201 }
1202 if !tc.storeV2Nil {
1203 storeV2Members, _ := membersFromStore(lg, st)
1204 assert.Equal(t, map[types.ID]*Member{}, storeV2Members)
1205 }
1206 if !tc.backendNil {
1207 be.ForceCommit()
1208 beMembers, _ := mustReadMembersFromBackend(lg, be)
1209 assert.Equal(t, map[types.ID]*Member{}, beMembers)
1210 }
1211 })
1212 }
1213 }
1214
View as plain text