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 +}