1// Copyright 2015 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 membership 16 17import ( 18 "crypto/sha1" 19 "encoding/binary" 20 "fmt" 21 "math/rand" 22 "sort" 23 "time" 24 25 "github.com/coreos/pkg/capnslog" 26 "go.etcd.io/etcd/pkg/types" 27) 28 29var ( 30 plog = capnslog.NewPackageLogger("go.etcd.io/etcd/v3", "etcdserver/membership") 31) 32 33// RaftAttributes represents the raft related attributes of an etcd member. 34type RaftAttributes struct { 35 // PeerURLs is the list of peers in the raft cluster. 36 // TODO(philips): ensure these are URLs 37 PeerURLs []string `json:"peerURLs"` 38 // IsLearner indicates if the member is raft learner. 39 IsLearner bool `json:"isLearner,omitempty"` 40} 41 42// Attributes represents all the non-raft related attributes of an etcd member. 43type Attributes struct { 44 Name string `json:"name,omitempty"` 45 ClientURLs []string `json:"clientURLs,omitempty"` 46} 47 48type Member struct { 49 ID types.ID `json:"id"` 50 RaftAttributes 51 Attributes 52} 53 54// NewMember creates a Member without an ID and generates one based on the 55// cluster name, peer URLs, and time. This is used for bootstrapping/adding new member. 56func NewMember(name string, peerURLs types.URLs, clusterName string, now *time.Time) *Member { 57 return newMember(name, peerURLs, clusterName, now, false) 58} 59 60// NewMemberAsLearner creates a learner Member without an ID and generates one based on the 61// cluster name, peer URLs, and time. This is used for adding new learner member. 62func NewMemberAsLearner(name string, peerURLs types.URLs, clusterName string, now *time.Time) *Member { 63 return newMember(name, peerURLs, clusterName, now, true) 64} 65 66func newMember(name string, peerURLs types.URLs, clusterName string, now *time.Time, isLearner bool) *Member { 67 m := &Member{ 68 RaftAttributes: RaftAttributes{ 69 PeerURLs: peerURLs.StringSlice(), 70 IsLearner: isLearner, 71 }, 72 Attributes: Attributes{Name: name}, 73 } 74 75 var b []byte 76 sort.Strings(m.PeerURLs) 77 for _, p := range m.PeerURLs { 78 b = append(b, []byte(p)...) 79 } 80 81 b = append(b, []byte(clusterName)...) 82 if now != nil { 83 b = append(b, []byte(fmt.Sprintf("%d", now.Unix()))...) 84 } 85 86 hash := sha1.Sum(b) 87 m.ID = types.ID(binary.BigEndian.Uint64(hash[:8])) 88 return m 89} 90 91// PickPeerURL chooses a random address from a given Member's PeerURLs. 92// It will panic if there is no PeerURLs available in Member. 93func (m *Member) PickPeerURL() string { 94 if len(m.PeerURLs) == 0 { 95 panic("member should always have some peer url") 96 } 97 return m.PeerURLs[rand.Intn(len(m.PeerURLs))] 98} 99 100func (m *Member) Clone() *Member { 101 if m == nil { 102 return nil 103 } 104 mm := &Member{ 105 ID: m.ID, 106 RaftAttributes: RaftAttributes{ 107 IsLearner: m.IsLearner, 108 }, 109 Attributes: Attributes{ 110 Name: m.Name, 111 }, 112 } 113 if m.PeerURLs != nil { 114 mm.PeerURLs = make([]string, len(m.PeerURLs)) 115 copy(mm.PeerURLs, m.PeerURLs) 116 } 117 if m.ClientURLs != nil { 118 mm.ClientURLs = make([]string, len(m.ClientURLs)) 119 copy(mm.ClientURLs, m.ClientURLs) 120 } 121 return mm 122} 123 124func (m *Member) IsStarted() bool { 125 return len(m.Name) != 0 126} 127 128// MembersByID implements sort by ID interface 129type MembersByID []*Member 130 131func (ms MembersByID) Len() int { return len(ms) } 132func (ms MembersByID) Less(i, j int) bool { return ms[i].ID < ms[j].ID } 133func (ms MembersByID) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } 134 135// MembersByPeerURLs implements sort by peer urls interface 136type MembersByPeerURLs []*Member 137 138func (ms MembersByPeerURLs) Len() int { return len(ms) } 139func (ms MembersByPeerURLs) Less(i, j int) bool { 140 return ms[i].PeerURLs[0] < ms[j].PeerURLs[0] 141} 142func (ms MembersByPeerURLs) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] } 143