kubeadm: increase ut coverage for util/etcd

Signed-off-by: xin.li <xin.li@daocloud.io>
This commit is contained in:
xin.li 2023-12-17 18:31:36 +08:00
parent e6acd1caf5
commit 430fd83454
2 changed files with 306 additions and 5 deletions

View File

@ -104,6 +104,8 @@ type Client struct {
Endpoints []string
newEtcdClient func(endpoints []string) (etcdClient, error)
listMembersFunc func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error)
}
// New creates a new EtcdCluster client
@ -135,6 +137,8 @@ func New(endpoints []string, ca, cert, key string) (*Client, error) {
})
}
client.listMembersFunc = client.listMembers
return &client, nil
}
@ -274,11 +278,14 @@ type Member struct {
PeerURL string
}
func (c *Client) listMembers() (*clientv3.MemberListResponse, error) {
func (c *Client) listMembers(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
// Gets the member list
var lastError error
var resp *clientv3.MemberListResponse
err := wait.ExponentialBackoff(etcdBackoff, func() (bool, error) {
if backoff == nil {
backoff = &etcdBackoff
}
err := wait.ExponentialBackoff(*backoff, func() (bool, error) {
cli, err := c.newEtcdClient(c.Endpoints)
if err != nil {
lastError = err
@ -304,7 +311,7 @@ func (c *Client) listMembers() (*clientv3.MemberListResponse, error) {
// GetMemberID returns the member ID of the given peer URL
func (c *Client) GetMemberID(peerURL string) (uint64, error) {
resp, err := c.listMembers()
resp, err := c.listMembersFunc(nil)
if err != nil {
return 0, err
}
@ -319,7 +326,7 @@ func (c *Client) GetMemberID(peerURL string) (uint64, error) {
// ListMembers returns the member list.
func (c *Client) ListMembers() ([]Member, error) {
resp, err := c.listMembers()
resp, err := c.listMembersFunc(nil)
if err != nil {
return nil, err
}
@ -480,7 +487,7 @@ func (c *Client) addMember(name string, peerAddrs string, isLearner bool) ([]Mem
// isLearner returns true if the given member ID is a learner.
func (c *Client) isLearner(memberID uint64) (bool, error) {
resp, err := c.listMembers()
resp, err := c.listMembersFunc(nil)
if err != nil {
return false, err
}

View File

@ -482,6 +482,11 @@ func TestClient_GetMemberID(t *testing.T) {
Endpoints: tt.fields.Endpoints,
newEtcdClient: tt.fields.newEtcdClient,
}
c.listMembersFunc = func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
f, _ := c.newEtcdClient([]string{})
resp, _ := f.MemberList(context.TODO())
return resp, nil
}
got, err := c.GetMemberID(tt.args.peerURL)
if !errors.Is(tt.wantErr, err) {
@ -494,3 +499,292 @@ func TestClient_GetMemberID(t *testing.T) {
})
}
}
func TestListMembers(t *testing.T) {
type fields struct {
Endpoints []string
newEtcdClient func(endpoints []string) (etcdClient, error)
listMembersFunc func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error)
}
tests := []struct {
name string
fields fields
want []Member
wantError bool
}{
{
name: "PeerURLs are empty",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{}
return f, nil
},
},
want: []Member{},
},
{
name: "PeerURLs are non-empty",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
ID: 1,
Name: "member1",
PeerURLs: []string{
"https://member1:2380",
},
},
{
ID: 2,
Name: "member2",
PeerURLs: []string{
"https://member2:2380",
},
},
},
}
return f, nil
},
},
want: []Member{
{
Name: "member1",
PeerURL: "https://member1:2380",
},
{
Name: "member2",
PeerURL: "https://member2:2380",
},
},
},
{
name: "PeerURLs has multiple urls",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
ID: 1,
Name: "member1",
PeerURLs: []string{
"https://member1:2380",
"https://member2:2380",
},
},
},
}
return f, nil
},
},
want: []Member{
{
Name: "member1",
PeerURL: "https://member1:2380",
},
},
},
{
name: "ListMembers return error",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
ID: 1,
Name: "member1",
PeerURLs: []string{
"https://member1:2380",
"https://member2:2380",
},
},
},
}
return f, nil
},
listMembersFunc: func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
return nil, errNotImplemented
},
},
want: nil,
wantError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
Endpoints: tt.fields.Endpoints,
newEtcdClient: tt.fields.newEtcdClient,
listMembersFunc: tt.fields.listMembersFunc,
}
if c.listMembersFunc == nil {
c.listMembersFunc = func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
return c.listMembers(&wait.Backoff{Steps: 1})
}
}
got, err := c.ListMembers()
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ListMembers() = %v, want %v", got, tt.want)
}
if (err != nil) != (tt.wantError) {
t.Errorf("ListMembers() error = %v, wantError %v", err, tt.wantError)
}
})
}
}
func TestIsLearner(t *testing.T) {
type fields struct {
Endpoints []string
newEtcdClient func(endpoints []string) (etcdClient, error)
listMembersFunc func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error)
}
tests := []struct {
name string
fields fields
memberID uint64
want bool
wantError bool
}{
{
name: "The specified member is not a learner",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
ID: 1,
Name: "member1",
PeerURLs: []string{
"https://member1:2380",
},
IsLearner: false,
},
},
}
return f, nil
},
},
memberID: 1,
want: false,
},
{
name: "The specified member is a learner",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
ID: 1,
Name: "member1",
PeerURLs: []string{
"https://member1:2380",
},
IsLearner: true,
},
{
ID: 2,
Name: "member2",
PeerURLs: []string{
"https://member2:2380",
},
},
},
}
return f, nil
},
},
memberID: 1,
want: true,
},
{
name: "The specified member does not exist",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{},
}
return f, nil
},
},
memberID: 3,
want: false,
},
{
name: "Learner ID is empty",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
Name: "member2",
PeerURLs: []string{
"https://member2:2380",
},
IsLearner: true,
},
},
}
return f, nil
},
},
want: true,
},
{
name: "ListMembers returns an error",
fields: fields{
Endpoints: []string{},
newEtcdClient: func(endpoints []string) (etcdClient, error) {
f := &fakeEtcdClient{
members: []*pb.Member{
{
Name: "member2",
PeerURLs: []string{
"https://member2:2380",
},
IsLearner: true,
},
},
}
return f, nil
},
listMembersFunc: func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
return nil, errNotImplemented
},
},
want: false,
wantError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
Endpoints: tt.fields.Endpoints,
newEtcdClient: tt.fields.newEtcdClient,
listMembersFunc: tt.fields.listMembersFunc,
}
if c.listMembersFunc == nil {
c.listMembersFunc = func(backoff *wait.Backoff) (*clientv3.MemberListResponse, error) {
f, _ := c.newEtcdClient([]string{})
resp, _ := f.MemberList(context.TODO())
return resp, nil
}
}
got, err := c.isLearner(tt.memberID)
if got != tt.want {
t.Errorf("isLearner() = %v, want %v", got, tt.want)
}
if (err != nil) != (tt.wantError) {
t.Errorf("isLearner() error = %v, wantError %v", err, tt.wantError)
}
})
}
}