1// Copyright 2016 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 15package clientv3 16 17import ( 18 "context" 19 20 pb "go.etcd.io/etcd/etcdserver/etcdserverpb" 21 "go.etcd.io/etcd/pkg/types" 22 23 "google.golang.org/grpc" 24) 25 26type ( 27 Member pb.Member 28 MemberListResponse pb.MemberListResponse 29 MemberAddResponse pb.MemberAddResponse 30 MemberRemoveResponse pb.MemberRemoveResponse 31 MemberUpdateResponse pb.MemberUpdateResponse 32 MemberPromoteResponse pb.MemberPromoteResponse 33) 34 35type Cluster interface { 36 // MemberList lists the current cluster membership. 37 MemberList(ctx context.Context) (*MemberListResponse, error) 38 39 // MemberAdd adds a new member into the cluster. 40 MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) 41 42 // MemberAddAsLearner adds a new learner member into the cluster. 43 MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) 44 45 // MemberRemove removes an existing member from the cluster. 46 MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) 47 48 // MemberUpdate updates the peer addresses of the member. 49 MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) 50 51 // MemberPromote promotes a member from raft learner (non-voting) to raft voting member. 52 MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) 53} 54 55type cluster struct { 56 remote pb.ClusterClient 57 callOpts []grpc.CallOption 58} 59 60func NewCluster(c *Client) Cluster { 61 api := &cluster{remote: RetryClusterClient(c)} 62 if c != nil { 63 api.callOpts = c.callOpts 64 } 65 return api 66} 67 68func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster { 69 api := &cluster{remote: remote} 70 if c != nil { 71 api.callOpts = c.callOpts 72 } 73 return api 74} 75 76func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) { 77 return c.memberAdd(ctx, peerAddrs, false) 78} 79 80func (c *cluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) { 81 return c.memberAdd(ctx, peerAddrs, true) 82} 83 84func (c *cluster) memberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) { 85 // fail-fast before panic in rafthttp 86 if _, err := types.NewURLs(peerAddrs); err != nil { 87 return nil, err 88 } 89 90 r := &pb.MemberAddRequest{ 91 PeerURLs: peerAddrs, 92 IsLearner: isLearner, 93 } 94 resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...) 95 if err != nil { 96 return nil, toErr(ctx, err) 97 } 98 return (*MemberAddResponse)(resp), nil 99} 100 101func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) { 102 r := &pb.MemberRemoveRequest{ID: id} 103 resp, err := c.remote.MemberRemove(ctx, r, c.callOpts...) 104 if err != nil { 105 return nil, toErr(ctx, err) 106 } 107 return (*MemberRemoveResponse)(resp), nil 108} 109 110func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) { 111 // fail-fast before panic in rafthttp 112 if _, err := types.NewURLs(peerAddrs); err != nil { 113 return nil, err 114 } 115 116 // it is safe to retry on update. 117 r := &pb.MemberUpdateRequest{ID: id, PeerURLs: peerAddrs} 118 resp, err := c.remote.MemberUpdate(ctx, r, c.callOpts...) 119 if err == nil { 120 return (*MemberUpdateResponse)(resp), nil 121 } 122 return nil, toErr(ctx, err) 123} 124 125func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) { 126 // it is safe to retry on list. 127 resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{}, c.callOpts...) 128 if err == nil { 129 return (*MemberListResponse)(resp), nil 130 } 131 return nil, toErr(ctx, err) 132} 133 134func (c *cluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) { 135 r := &pb.MemberPromoteRequest{ID: id} 136 resp, err := c.remote.MemberPromote(ctx, r, c.callOpts...) 137 if err != nil { 138 return nil, toErr(ctx, err) 139 } 140 return (*MemberPromoteResponse)(resp), nil 141} 142