| // Copyright 2016 The etcd Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package v3rpc |
| |
| import ( |
| "context" |
| "time" |
| |
| "go.etcd.io/etcd/etcdserver" |
| "go.etcd.io/etcd/etcdserver/api" |
| "go.etcd.io/etcd/etcdserver/api/membership" |
| "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes" |
| pb "go.etcd.io/etcd/etcdserver/etcdserverpb" |
| "go.etcd.io/etcd/pkg/types" |
| ) |
| |
| type ClusterServer struct { |
| cluster api.Cluster |
| server etcdserver.ServerV3 |
| } |
| |
| func NewClusterServer(s etcdserver.ServerV3) *ClusterServer { |
| return &ClusterServer{ |
| cluster: s.Cluster(), |
| server: s, |
| } |
| } |
| |
| func (cs *ClusterServer) MemberAdd(ctx context.Context, r *pb.MemberAddRequest) (*pb.MemberAddResponse, error) { |
| urls, err := types.NewURLs(r.PeerURLs) |
| if err != nil { |
| return nil, rpctypes.ErrGRPCMemberBadURLs |
| } |
| |
| now := time.Now() |
| var m *membership.Member |
| if r.IsLearner { |
| m = membership.NewMemberAsLearner("", urls, "", &now) |
| } else { |
| m = membership.NewMember("", urls, "", &now) |
| } |
| membs, merr := cs.server.AddMember(ctx, *m) |
| if merr != nil { |
| return nil, togRPCError(merr) |
| } |
| |
| return &pb.MemberAddResponse{ |
| Header: cs.header(), |
| Member: &pb.Member{ |
| ID: uint64(m.ID), |
| PeerURLs: m.PeerURLs, |
| IsLearner: m.IsLearner, |
| }, |
| Members: membersToProtoMembers(membs), |
| }, nil |
| } |
| |
| func (cs *ClusterServer) MemberRemove(ctx context.Context, r *pb.MemberRemoveRequest) (*pb.MemberRemoveResponse, error) { |
| membs, err := cs.server.RemoveMember(ctx, r.ID) |
| if err != nil { |
| return nil, togRPCError(err) |
| } |
| return &pb.MemberRemoveResponse{Header: cs.header(), Members: membersToProtoMembers(membs)}, nil |
| } |
| |
| func (cs *ClusterServer) MemberUpdate(ctx context.Context, r *pb.MemberUpdateRequest) (*pb.MemberUpdateResponse, error) { |
| m := membership.Member{ |
| ID: types.ID(r.ID), |
| RaftAttributes: membership.RaftAttributes{PeerURLs: r.PeerURLs}, |
| } |
| membs, err := cs.server.UpdateMember(ctx, m) |
| if err != nil { |
| return nil, togRPCError(err) |
| } |
| return &pb.MemberUpdateResponse{Header: cs.header(), Members: membersToProtoMembers(membs)}, nil |
| } |
| |
| func (cs *ClusterServer) MemberList(ctx context.Context, r *pb.MemberListRequest) (*pb.MemberListResponse, error) { |
| membs := membersToProtoMembers(cs.cluster.Members()) |
| return &pb.MemberListResponse{Header: cs.header(), Members: membs}, nil |
| } |
| |
| func (cs *ClusterServer) MemberPromote(ctx context.Context, r *pb.MemberPromoteRequest) (*pb.MemberPromoteResponse, error) { |
| membs, err := cs.server.PromoteMember(ctx, r.ID) |
| if err != nil { |
| return nil, togRPCError(err) |
| } |
| return &pb.MemberPromoteResponse{Header: cs.header(), Members: membersToProtoMembers(membs)}, nil |
| } |
| |
| func (cs *ClusterServer) header() *pb.ResponseHeader { |
| return &pb.ResponseHeader{ClusterId: uint64(cs.cluster.ID()), MemberId: uint64(cs.server.ID()), RaftTerm: cs.server.Term()} |
| } |
| |
| func membersToProtoMembers(membs []*membership.Member) []*pb.Member { |
| protoMembs := make([]*pb.Member, len(membs)) |
| for i := range membs { |
| protoMembs[i] = &pb.Member{ |
| Name: membs[i].Name, |
| ID: uint64(membs[i].ID), |
| PeerURLs: membs[i].PeerURLs, |
| ClientURLs: membs[i].ClientURLs, |
| IsLearner: membs[i].IsLearner, |
| } |
| } |
| return protoMembs |
| } |