From 36a76e8531b3fe35b13722549a0a774dee2d8d51 Mon Sep 17 00:00:00 2001 From: Chris Ayoub Date: Tue, 19 Jul 2022 11:36:41 -0400 Subject: [PATCH] clientv3: filter learners members during autosync This change is to ensure that all members returned during the client's AutoSync are started and are not learners, which are not valid etcd members to make requests to. Signed-off-by: Chris Ayoub --- clientv3/client.go | 4 +++- clientv3/client_test.go | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/clientv3/client.go b/clientv3/client.go index a35ec679a..e2003bcfb 100644 --- a/clientv3/client.go +++ b/clientv3/client.go @@ -174,7 +174,9 @@ func (c *Client) Sync(ctx context.Context) error { } var eps []string for _, m := range mresp.Members { - eps = append(eps, m.ClientURLs...) + if len(m.Name) != 0 && !m.IsLearner { + eps = append(eps, m.ClientURLs...) + } } c.SetEndpoints(eps...) return nil diff --git a/clientv3/client_test.go b/clientv3/client_test.go index de7c59229..a6d9fc439 100644 --- a/clientv3/client_test.go +++ b/clientv3/client_test.go @@ -22,6 +22,7 @@ import ( "time" "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes" + "go.etcd.io/etcd/etcdserver/etcdserverpb" "go.etcd.io/etcd/pkg/testutil" "google.golang.org/grpc" @@ -166,3 +167,51 @@ func TestCloseCtxClient(t *testing.T) { t.Errorf("failed to Close the client. %v", err) } } + +func TestSyncFiltersMembers(t *testing.T) { + defer testutil.AfterTest(t) + + c, _ := New(Config{Endpoints: []string{"http://254.0.0.1:12345"}}) + c.Cluster = &mockCluster{ + []*etcdserverpb.Member{ + {ID: 0, Name: "", ClientURLs: []string{"http://254.0.0.1:12345"}, IsLearner: false}, + {ID: 1, Name: "isStarted", ClientURLs: []string{"http://254.0.0.2:12345"}, IsLearner: true}, + {ID: 2, Name: "isStartedAndNotLearner", ClientURLs: []string{"http://254.0.0.3:12345"}, IsLearner: false}, + }, + } + c.Sync(context.Background()) + + endpoints := c.Endpoints() + if len(endpoints) != 1 || endpoints[0] != "http://254.0.0.3:12345" { + t.Error("Client.Sync uses learner and/or non-started member client URLs") + } + c.Close() +} + +type mockCluster struct { + members []*etcdserverpb.Member +} + +func (mc *mockCluster) MemberList(ctx context.Context) (*MemberListResponse, error) { + return &MemberListResponse{Members: mc.members}, nil +} + +func (mc *mockCluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) { + return nil, nil +} + +func (mc *mockCluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) { + return nil, nil +} + +func (mc *mockCluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) { + return nil, nil +} + +func (mc *mockCluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) { + return nil, nil +} + +func (mc *mockCluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) { + return nil, nil +}