...

Source file src/go.etcd.io/etcd/server/v3/etcdserver/api/membership/cluster_test.go

Documentation: go.etcd.io/etcd/server/v3/etcdserver/api/membership

     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  
    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  		// single peer with a single address
   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  		// single peer with a single address with a port
   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  		// several members explicitly unsorted
   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  		// no members
   133  		{
   134  			mems:  []*Member{},
   135  			wurls: []string{},
   136  		},
   137  
   138  		// peer with no peer urls
   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  		// single peer with a single address
   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  		// single peer with a single address with a port
   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  		// several members explicitly unsorted
   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  		// no members
   188  		{
   189  			mems:  []*Member{},
   190  			wurls: []string{},
   191  		},
   192  
   193  		// peer with no client urls
   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  			// unmatched length
   218  			[]*Member{
   219  				newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
   220  			},
   221  			[]*Member{},
   222  		},
   223  		{
   224  			// unmatched peer urls
   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  			// unmatched peer urls
   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  		// try to change the peer url of 2 to the peer url of 3
   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  		// update attributes of existing member
   580  		{
   581  			[]*Member{
   582  				newTestMember(1, nil, "", nil),
   583  			},
   584  			nil,
   585  			[]*Member{
   586  				newTestMember(1, nil, name, clientURLs),
   587  			},
   588  		},
   589  		// update attributes of removed member
   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  			// 0/3 members ready, should fail
   639  			[]*Member{
   640  				newTestMember(1, nil, "", nil),
   641  				newTestMember(2, nil, "", nil),
   642  				newTestMember(3, nil, "", nil),
   643  			},
   644  			false,
   645  		},
   646  		{
   647  			// 1/2 members ready, should fail
   648  			[]*Member{
   649  				newTestMember(1, nil, "1", nil),
   650  				newTestMember(2, nil, "", nil),
   651  			},
   652  			false,
   653  		},
   654  		{
   655  			// 1/3 members ready, should fail
   656  			[]*Member{
   657  				newTestMember(1, nil, "1", nil),
   658  				newTestMember(2, nil, "", nil),
   659  				newTestMember(3, nil, "", nil),
   660  			},
   661  			false,
   662  		},
   663  		{
   664  			// 1/1 members ready, should succeed (special case of 1-member cluster for recovery)
   665  			[]*Member{
   666  				newTestMember(1, nil, "1", nil),
   667  			},
   668  			true,
   669  		},
   670  		{
   671  			// 2/3 members ready, should fail
   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  			// 3/3 members ready, should be fine to add one member and retain quorum
   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  			// 3/4 members ready, should be fine to add one member and retain quorum
   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  			// empty cluster, it is impossible but should fail
   700  			[]*Member{},
   701  			false,
   702  		},
   703  		{
   704  			// 2 voting members ready in cluster with 2 voting members and 2 unstarted learner member, should succeed
   705  			// (the status of learner members does not affect the readiness of adding voting member)
   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  			// 1 voting member ready in cluster with 2 voting members and 2 ready learner member, should fail
   716  			// (the status of learner members does not affect the readiness of adding voting member)
   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  			// 1/1 members ready, should fail
   742  			[]*Member{
   743  				newTestMember(1, nil, "1", nil),
   744  			},
   745  			1,
   746  			false,
   747  		},
   748  		{
   749  			// 0/3 members ready, should fail
   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  			// 1/2 members ready, should be fine to remove unstarted member
   760  			// (isReadyToRemoveMember() logic should return success, but operation itself would fail)
   761  			[]*Member{
   762  				newTestMember(1, nil, "1", nil),
   763  				newTestMember(2, nil, "", nil),
   764  			},
   765  			2,
   766  			true,
   767  		},
   768  		{
   769  			// 2/3 members ready, should fail
   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  			// 3/3 members ready, should be fine to remove one member and retain quorum
   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  			// 3/4 members ready, should be fine to remove one member
   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  			// 3/4 members ready, should be fine to remove unstarted member
   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  			// 1 voting members ready in cluster with 1 voting member and 1 ready learner,
   812  			// removing voting member should fail
   813  			// (the status of learner members does not affect the readiness of removing voting member)
   814  			[]*Member{
   815  				newTestMember(1, nil, "1", nil),
   816  				newTestMemberAsLearner(2, nil, "2", nil),
   817  			},
   818  			1,
   819  			false,
   820  		},
   821  		{
   822  			// 1 voting members ready in cluster with 2 voting member and 1 ready learner,
   823  			// removing ready voting member should fail
   824  			// (the status of learner members does not affect the readiness of removing voting member)
   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  			// 1 voting members ready in cluster with 2 voting member and 1 ready learner,
   835  			// removing unstarted voting member should be fine. (Actual operation will fail)
   836  			// (the status of learner members does not affect the readiness of removing voting member)
   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  			// 1 voting members ready in cluster with 2 voting member and 1 unstarted learner,
   847  			// removing not-ready voting member should be fine. (Actual operation will fail)
   848  			// (the status of learner members does not affect the readiness of removing voting member)
   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  			// 1/1 members ready, should succeed (quorum = 1, new quorum = 2)
   874  			[]*Member{
   875  				newTestMember(1, nil, "1", nil),
   876  				newTestMemberAsLearner(2, nil, "2", nil),
   877  			},
   878  			2,
   879  			true,
   880  		},
   881  		{
   882  			// 0/1 members ready, should fail (quorum = 1)
   883  			[]*Member{
   884  				newTestMember(1, nil, "", nil),
   885  				newTestMemberAsLearner(2, nil, "2", nil),
   886  			},
   887  			2,
   888  			false,
   889  		},
   890  		{
   891  			// 2/2 members ready, should succeed (quorum = 2)
   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  			// 1/2 members ready, should succeed (quorum = 2)
   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  			// 1/3 members ready, should fail (quorum = 2)
   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  			// 2/3 members ready, should succeed (quorum = 2, new quorum = 3)
   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  			// 2/4 members ready, should succeed (quorum = 3)
   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