diff --git a/client/v3/client.go b/client/v3/client.go index ba08cf5bbdf..2990379ab9f 100644 --- a/client/v3/client.go +++ b/client/v3/client.go @@ -185,7 +185,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/client/v3/client_test.go b/client/v3/client_test.go index 0804f05c5c4..8e2c03e280b 100644 --- a/client/v3/client_test.go +++ b/client/v3/client_test.go @@ -17,13 +17,14 @@ package clientv3 import ( "context" "fmt" - "go.uber.org/zap" "net" "testing" "time" + "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" "go.etcd.io/etcd/client/pkg/v3/testutil" + "go.uber.org/zap" "go.uber.org/zap/zaptest" "google.golang.org/grpc" @@ -198,3 +199,49 @@ func TestZapWithLogger(t *testing.T) { t.Errorf("WithZapLogger should modify *zap.Logger") } } + +func TestSyncFiltersMembers(t *testing.T) { + c, _ := NewClient(t, Config{Endpoints: []string{"http://254.0.0.1:12345"}}) + defer c.Close() + 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") + } +} + +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 +}